diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs index 87a8d47395..8fcf875ce5 100644 --- a/crates/wasm-compose/src/encoding.rs +++ b/crates/wasm-compose/src/encoding.rs @@ -649,6 +649,9 @@ impl<'a> TypeEncoder<'a> { ComponentDefinedType::Record(r) => self.record(state, r), ComponentDefinedType::Variant(v) => self.variant(state, v), ComponentDefinedType::List(ty) => self.list(state, *ty), + ComponentDefinedType::FixedSizeList(ty, elements) => { + self.fixed_size_list(state, *ty, *elements) + } ComponentDefinedType::Tuple(t) => self.tuple(state, t), ComponentDefinedType::Flags(names) => Self::flags(&mut state.cur.encodable, names), ComponentDefinedType::Enum(cases) => Self::enum_type(&mut state.cur.encodable, cases), @@ -709,6 +712,23 @@ impl<'a> TypeEncoder<'a> { index } + fn fixed_size_list( + &self, + state: &mut TypeState<'a>, + ty: ct::ComponentValType, + elements: u32, + ) -> u32 { + let ty = self.component_val_type(state, ty); + let index = state.cur.encodable.type_count(); + state + .cur + .encodable + .ty() + .defined_type() + .fixed_size_list(ty, elements); + index + } + fn tuple(&self, state: &mut TypeState<'a>, tuple: &TupleType) -> u32 { let types = tuple .types @@ -1228,7 +1248,9 @@ impl DependencyRegistrar<'_, '_> { ComponentDefinedType::Primitive(_) | ComponentDefinedType::Enum(_) | ComponentDefinedType::Flags(_) => {} - ComponentDefinedType::List(t) | ComponentDefinedType::Option(t) => self.val_type(*t), + ComponentDefinedType::List(t) + | ComponentDefinedType::FixedSizeList(t, _) + | ComponentDefinedType::Option(t) => self.val_type(*t), ComponentDefinedType::Own(r) | ComponentDefinedType::Borrow(r) => { self.ty(ComponentAnyTypeId::Resource(*r)) } diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs index e32c75c464..6d6a8f7b1a 100644 --- a/crates/wasm-encoder/src/component/types.rs +++ b/crates/wasm-encoder/src/component/types.rs @@ -593,6 +593,13 @@ impl ComponentDefinedTypeEncoder<'_> { ty.into().encode(self.0); } + /// Define a fixed size list type. + pub fn fixed_size_list(self, ty: impl Into, elements: u32) { + self.0.push(0x67); + ty.into().encode(self.0); + elements.encode(self.0); + } + /// Define a tuple type. pub fn tuple(self, types: I) where diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs index 3d50dfca28..7fd3578ddc 100644 --- a/crates/wasm-encoder/src/reencode/component.rs +++ b/crates/wasm-encoder/src/reencode/component.rs @@ -767,6 +767,9 @@ pub mod component_utils { wasmparser::ComponentDefinedType::List(t) => { defined.list(reencoder.component_val_type(t)); } + wasmparser::ComponentDefinedType::FixedSizeList(t, elements) => { + defined.fixed_size_list(reencoder.component_val_type(t), elements); + } wasmparser::ComponentDefinedType::Tuple(t) => { defined.tuple(t.iter().map(|t| reencoder.component_val_type(*t))); } diff --git a/crates/wasm-wave/src/value/ty.rs b/crates/wasm-wave/src/value/ty.rs index 8898892b4a..852051971e 100644 --- a/crates/wasm-wave/src/value/ty.rs +++ b/crates/wasm-wave/src/value/ty.rs @@ -10,6 +10,7 @@ pub struct Type(pub(super) TypeEnum); pub(super) enum TypeEnum { Simple(SimpleType), List(Arc), + FixedSizeList(Arc, u32), Record(Arc), Tuple(Arc), Variant(Arc), @@ -55,6 +56,15 @@ impl Type { Self(TypeEnum::List(Arc::new(ListType { element }))) } + /// Returns a list type with the given element type. + pub fn fixed_size_list(element_type: impl Into, elements: u32) -> Self { + let element = element_type.into(); + Self(TypeEnum::FixedSizeList( + Arc::new(ListType { element }), + elements, + )) + } + /// Returns a record type with the given field types. Returns None if /// `fields` is empty. pub fn record>>( @@ -189,6 +199,7 @@ impl WasmType for Type { match self.0 { TypeEnum::Simple(simple) => simple.0, TypeEnum::List(_) => WasmTypeKind::List, + TypeEnum::FixedSizeList(_, _) => WasmTypeKind::FixedSizeList, TypeEnum::Record(_) => WasmTypeKind::Record, TypeEnum::Tuple(_) => WasmTypeKind::Tuple, TypeEnum::Variant(_) => WasmTypeKind::Variant, diff --git a/crates/wasm-wave/src/value/wit.rs b/crates/wasm-wave/src/value/wit.rs index 423090a622..d0822f9713 100644 --- a/crates/wasm-wave/src/value/wit.rs +++ b/crates/wasm-wave/src/value/wit.rs @@ -70,6 +70,9 @@ impl<'a> TypeResolver<'a> { TypeDefKind::Option(some_type) => self.resolve_option(some_type), TypeDefKind::Result(result) => self.resolve_result(result), TypeDefKind::List(element_type) => self.resolve_list(element_type), + TypeDefKind::FixedSizeList(element_type, elements) => { + self.resolve_fixed_size_list(element_type, *elements) + } TypeDefKind::Type(Type::Bool) => Ok(value::Type::BOOL), TypeDefKind::Type(Type::U8) => Ok(value::Type::U8), TypeDefKind::Type(Type::U16) => Ok(value::Type::U16), @@ -145,6 +148,11 @@ impl<'a> TypeResolver<'a> { let element_type = self.resolve_type(*element_type)?; Ok(value::Type::list(element_type)) } + + fn resolve_fixed_size_list(&self, element_type: &Type, elements: u32) -> ValueResult { + let element_type = self.resolve_type(*element_type)?; + Ok(value::Type::fixed_size_list(element_type, elements)) + } } #[cfg(test)] diff --git a/crates/wasm-wave/src/wasm/ty.rs b/crates/wasm-wave/src/wasm/ty.rs index e131759cfb..499647a072 100644 --- a/crates/wasm-wave/src/wasm/ty.rs +++ b/crates/wasm-wave/src/wasm/ty.rs @@ -20,6 +20,7 @@ pub enum WasmTypeKind { Char, String, List, + FixedSizeList, Record, Tuple, Variant, @@ -48,6 +49,7 @@ impl std::fmt::Display for WasmTypeKind { WasmTypeKind::Char => "char", WasmTypeKind::String => "string", WasmTypeKind::List => "list", + WasmTypeKind::FixedSizeList => "list<_,N>", WasmTypeKind::Record => "record", WasmTypeKind::Tuple => "tuple", WasmTypeKind::Variant => "variant", diff --git a/crates/wasm-wave/src/writer.rs b/crates/wasm-wave/src/writer.rs index eca4f37a14..4233f359ed 100644 --- a/crates/wasm-wave/src/writer.rs +++ b/crates/wasm-wave/src/writer.rs @@ -75,6 +75,16 @@ impl Writer { } self.write_str("]") } + WasmTypeKind::FixedSizeList => { + self.write_str("[")?; + for (idx, val) in val.unwrap_list().enumerate() { + if idx != 0 { + self.write_str(", ")?; + } + self.write_value(&*val)?; + } + self.write_str("]") + } WasmTypeKind::Record => { self.write_str("{")?; let mut first = true; diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs index fbe88a38f9..c0d5e8e0c6 100644 --- a/crates/wasmparser/src/features.rs +++ b/crates/wasmparser/src/features.rs @@ -254,6 +254,11 @@ define_wasm_features! { /// Corresponds to the 📝 character in /// . pub cm_error_context: CM_ERROR_CONTEXT(1 << 30) = false; + /// Support for fixed size lists + /// + /// Corresponds to the 🔧 character in + /// . + pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 31) = false; } } diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs index 0bdc1df956..888a977c95 100644 --- a/crates/wasmparser/src/readers/component/types.rs +++ b/crates/wasmparser/src/readers/component/types.rs @@ -441,6 +441,8 @@ pub enum ComponentDefinedType<'a> { Variant(Box<[VariantCase<'a>]>), /// The type is a list of the given value type. List(ComponentValType), + /// The type is a fixed size list of the given value type. + FixedSizeList(ComponentValType, u32), /// The type is a tuple of the given value types. Tuple(Box<[ComponentValType]>), /// The type is flags with the given names. @@ -503,8 +505,9 @@ impl<'a> ComponentDefinedType<'a> { }, 0x69 => ComponentDefinedType::Own(reader.read()?), 0x68 => ComponentDefinedType::Borrow(reader.read()?), - 0x65 => ComponentDefinedType::Future(reader.read()?), + 0x67 => ComponentDefinedType::FixedSizeList(reader.read()?, reader.read_var_u32()?), 0x66 => ComponentDefinedType::Stream(reader.read()?), + 0x65 => ComponentDefinedType::Future(reader.read()?), x => return reader.invalid_leading_byte(x, "component defined type"), }) } diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 514198af8a..8436110797 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -724,9 +724,9 @@ impl ComponentState { .map(|t| types.type_named_valtype(t, set)) .unwrap_or(true) } - ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { - types.type_named_valtype(ty, set) - } + ComponentDefinedType::List(ty) + | ComponentDefinedType::FixedSizeList(ty, _) + | ComponentDefinedType::Option(ty) => types.type_named_valtype(ty, set), // The resource referred to by own/borrow must be named. ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => { @@ -3475,6 +3475,21 @@ impl ComponentState { crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List( self.create_component_val_type(ty, offset)?, )), + crate::ComponentDefinedType::FixedSizeList(ty, elements) => { + if !self.features.cm_fixed_size_list() { + bail!( + offset, + "Fixed size lists require the component model fixed size list feature" + ) + } + if elements < 1 { + bail!(offset, "Fixed size lists must have more than zero elements") + } + Ok(ComponentDefinedType::FixedSizeList( + self.create_component_val_type(ty, offset)?, + elements, + )) + } crate::ComponentDefinedType::Tuple(tys) => { self.create_tuple_type(tys.as_ref(), types, offset) } diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs index 271b850fc6..2f14b37134 100644 --- a/crates/wasmparser/src/validator/component_types.rs +++ b/crates/wasmparser/src/validator/component_types.rs @@ -1084,6 +1084,8 @@ pub enum ComponentDefinedType { Variant(VariantType), /// The type is a list. List(ComponentValType), + /// The type is a fixed size list. + FixedSizeList(ComponentValType, u32), /// The type is a tuple. Tuple(TupleType), /// The type is a set of flags. @@ -1124,7 +1126,7 @@ impl TypeData for ComponentDefinedType { Self::Record(r) => r.info, Self::Variant(v) => v.info, Self::Tuple(t) => t.info, - Self::List(ty) | Self::Option(ty) => ty.info(types), + Self::List(ty) | Self::FixedSizeList(ty, _) | Self::Option(ty) => ty.info(types), Self::Result { ok, err } => { let default = TypeInfo::new(); let mut info = ok.map(|ty| ty.type_info(types)).unwrap_or(default); @@ -1153,7 +1155,7 @@ impl ComponentDefinedType { | Self::Borrow(_) | Self::Future(_) | Self::Stream(_) => false, - Self::Option(ty) => ty.contains_ptr(types), + Self::Option(ty) | Self::FixedSizeList(ty, _) => ty.contains_ptr(types), Self::Result { ok, err } => { ok.map(|ty| ty.contains_ptr(types)).unwrap_or(false) || err.map(|ty| ty.contains_ptr(types)).unwrap_or(false) @@ -1174,6 +1176,12 @@ impl ComponentDefinedType { lowered_types, ), Self::List(_) => lowered_types.push(ValType::I32) && lowered_types.push(ValType::I32), + Self::FixedSizeList(ty, length) => { + if *length as usize > lowered_types.max { + return false; + } + (0..*length).all(|_n| ty.push_wasm_types(types, lowered_types)) + } Self::Tuple(t) => t .types .iter() @@ -1248,6 +1256,7 @@ impl ComponentDefinedType { ComponentDefinedType::Flags(_) => "flags", ComponentDefinedType::Option(_) => "option", ComponentDefinedType::List(_) => "list", + ComponentDefinedType::FixedSizeList(_, _) => "fixed size list", ComponentDefinedType::Result { .. } => "result", ComponentDefinedType::Own(_) => "own", ComponentDefinedType::Borrow(_) => "borrow", @@ -1985,7 +1994,9 @@ impl TypeAlloc { } } } - ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { + ComponentDefinedType::List(ty) + | ComponentDefinedType::FixedSizeList(ty, _) + | ComponentDefinedType::Option(ty) => { self.free_variables_valtype(ty, set); } ComponentDefinedType::Result { ok, err } => { @@ -2127,9 +2138,9 @@ impl TypeAlloc { .map(|t| self.type_named_valtype(t, set)) .unwrap_or(true) } - ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { - self.type_named_valtype(ty, set) - } + ComponentDefinedType::List(ty) + | ComponentDefinedType::FixedSizeList(ty, _) + | ComponentDefinedType::Option(ty) => self.type_named_valtype(ty, set), // own/borrow themselves don't have to be named, but the resource // they refer to must be named. @@ -2314,7 +2325,9 @@ where } } } - ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => { + ComponentDefinedType::List(ty) + | ComponentDefinedType::FixedSizeList(ty, _) + | ComponentDefinedType::Option(ty) => { any_changed |= self.remap_valtype(ty, map); } ComponentDefinedType::Result { ok, err } => { @@ -3203,6 +3216,14 @@ impl<'a> SubtypeCx<'a> { (Variant(_), b) => bail!(offset, "expected {}, found variant", b.desc()), (List(a), List(b)) | (Option(a), Option(b)) => self.component_val_type(a, b, offset), (List(_), b) => bail!(offset, "expected {}, found list", b.desc()), + (FixedSizeList(a, asize), FixedSizeList(b, bsize)) => { + if asize != bsize { + bail!(offset, "expected fixed size {bsize}, found size {asize}") + } else { + self.component_val_type(a, b, offset) + } + } + (FixedSizeList(_, _), b) => bail!(offset, "expected {}, found list", b.desc()), (Option(_), b) => bail!(offset, "expected {}, found option", b.desc()), (Tuple(a), Tuple(b)) => { if a.types.len() != b.types.len() { diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs index f10591eafd..a834a10ccc 100644 --- a/crates/wasmprinter/src/component.rs +++ b/crates/wasmprinter/src/component.rs @@ -169,6 +169,19 @@ impl Printer<'_, '_> { Ok(()) } + pub(crate) fn print_fixed_size_list_type( + &mut self, + state: &State, + element_ty: &ComponentValType, + elements: u32, + ) -> Result<()> { + self.start_group("list ")?; + self.print_component_val_type(state, element_ty)?; + self.result.write_str(&format!(" {elements}"))?; + self.end_group()?; + Ok(()) + } + pub(crate) fn print_tuple_type( &mut self, state: &State, @@ -265,6 +278,9 @@ impl Printer<'_, '_> { ComponentDefinedType::Record(fields) => self.print_record_type(state, fields)?, ComponentDefinedType::Variant(cases) => self.print_variant_type(state, cases)?, ComponentDefinedType::List(ty) => self.print_list_type(state, ty)?, + ComponentDefinedType::FixedSizeList(ty, elements) => { + self.print_fixed_size_list_type(state, ty, *elements)? + } ComponentDefinedType::Tuple(tys) => self.print_tuple_type(state, tys)?, ComponentDefinedType::Flags(names) => self.print_flag_or_enum_type("flags", names)?, ComponentDefinedType::Enum(cases) => self.print_flag_or_enum_type("enum", cases)?, diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index 89d4f3fb4c..b80455f660 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -109,6 +109,9 @@ fn encode_defined_type(encoder: ComponentDefinedTypeEncoder, ty: &ComponentDefin ComponentDefinedType::List(l) => { encoder.list(l.element.as_ref()); } + ComponentDefinedType::FixedSizeList(l) => { + encoder.fixed_size_list(l.element.as_ref(), l.elements); + } ComponentDefinedType::Tuple(t) => { encoder.tuple(t.fields.iter()); } diff --git a/crates/wast/src/component/expand.rs b/crates/wast/src/component/expand.rs index 01b00d7038..0887fc4254 100644 --- a/crates/wast/src/component/expand.rs +++ b/crates/wast/src/component/expand.rs @@ -532,8 +532,12 @@ impl<'a> Expander<'a> { } } } - ComponentDefinedType::List(t) => { - self.expand_component_val_ty(&mut t.element); + ComponentDefinedType::List(List { element: t }) + | ComponentDefinedType::FixedSizeList(FixedSizeList { + element: t, + elements: _, + }) => { + self.expand_component_val_ty(t); } ComponentDefinedType::Tuple(t) => { for field in t.fields.iter_mut() { diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 21c0e56bda..95ef0ef678 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -547,8 +547,12 @@ impl<'a> Resolver<'a> { } } } - ComponentDefinedType::List(l) => { - self.component_val_type(&mut l.element)?; + ComponentDefinedType::List(List { element: t }) + | ComponentDefinedType::FixedSizeList(FixedSizeList { + element: t, + elements: _, + }) => { + self.component_val_type(t)?; } ComponentDefinedType::Tuple(t) => { for field in t.fields.iter_mut() { diff --git a/crates/wast/src/component/types.rs b/crates/wast/src/component/types.rs index 648eb59df1..a9ef3ba855 100644 --- a/crates/wast/src/component/types.rs +++ b/crates/wast/src/component/types.rs @@ -386,6 +386,7 @@ pub enum ComponentDefinedType<'a> { Record(Record<'a>), Variant(Variant<'a>), List(List<'a>), + FixedSizeList(FixedSizeList<'a>), Tuple(Tuple<'a>), Flags(Flags<'a>), Enum(Enum<'a>), @@ -405,7 +406,7 @@ impl<'a> ComponentDefinedType<'a> { } else if l.peek::()? { Ok(Self::Variant(parser.parse()?)) } else if l.peek::()? { - Ok(Self::List(parser.parse()?)) + parse_list(parser) } else if l.peek::()? { Ok(Self::Tuple(parser.parse()?)) } else if l.peek::()? { @@ -586,12 +587,28 @@ pub struct List<'a> { pub element: Box>, } -impl<'a> Parse<'a> for List<'a> { - fn parse(parser: Parser<'a>) -> Result { - parser.parse::()?; - Ok(Self { - element: Box::new(parser.parse()?), - }) +/// A fixed size list type. +#[derive(Debug)] +pub struct FixedSizeList<'a> { + /// The element type of the array. + pub element: Box>, + /// Number of Elements + pub elements: u32, +} + +fn parse_list<'a>(parser: Parser<'a>) -> Result> { + parser.parse::()?; + let tp = parser.parse()?; + let elements = parser.parse::>()?; + if let Some(elements) = elements { + Ok(ComponentDefinedType::FixedSizeList(FixedSizeList { + element: Box::new(tp), + elements, + })) + } else { + Ok(ComponentDefinedType::List(List { + element: Box::new(tp), + })) } } diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs index ce255ede98..f93d9a7a0f 100644 --- a/crates/wit-component/src/encoding.rs +++ b/crates/wit-component/src/encoding.rs @@ -320,6 +320,7 @@ impl TypeContents { } TypeDefKind::Enum(_) => Self::empty(), TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::LIST, + TypeDefKind::FixedSizeList(t, _elements) => Self::for_type(resolve, t), TypeDefKind::Type(t) => Self::for_type(resolve, t), TypeDefKind::Future(_) => Self::empty(), TypeDefKind::Stream(_) => Self::empty(), diff --git a/crates/wit-component/src/encoding/types.rs b/crates/wit-component/src/encoding/types.rs index 59d4330f52..7b354dc674 100644 --- a/crates/wit-component/src/encoding/types.rs +++ b/crates/wit-component/src/encoding/types.rs @@ -144,6 +144,12 @@ pub trait ValtypeEncoder<'a> { encoder.list(ty); ComponentValType::Type(index) } + TypeDefKind::FixedSizeList(ty, elements) => { + let ty = self.encode_valtype(resolve, ty)?; + let (index, encoder) = self.defined_type(); + encoder.fixed_size_list(ty, *elements); + ComponentValType::Type(index) + } TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?, TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?, TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?, diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index 3292fd0550..22669224b8 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -573,6 +573,13 @@ impl WitPrinter { self.print_type_name(resolve, ty)?; self.output.generic_args_end(); } + TypeDefKind::FixedSizeList(ty, size) => { + self.output.ty("list", TypeKind::BuiltIn); + self.output.generic_args_start(); + self.print_type_name(resolve, ty)?; + self.output.push_str(&format!(", {}", *size)); + self.output.generic_args_end(); + } TypeDefKind::Type(ty) => self.print_type_name(resolve, ty)?, TypeDefKind::Future(ty) => { if let Some(ty) = ty { @@ -747,6 +754,9 @@ impl WitPrinter { TypeDefKind::List(inner) => { self.declare_list(resolve, ty.name.as_deref(), inner)? } + TypeDefKind::FixedSizeList(inner, size) => { + self.declare_fixed_size_list(resolve, ty.name.as_deref(), inner, *size)? + } TypeDefKind::Type(inner) => match ty.name.as_deref() { Some(name) => { self.output.keyword("type"); @@ -960,6 +970,30 @@ impl WitPrinter { Ok(()) } + fn declare_fixed_size_list( + &mut self, + resolve: &Resolve, + name: Option<&str>, + ty: &Type, + elements: u32, + ) -> Result<()> { + if let Some(name) = name { + self.output.keyword("type"); + self.output.str(" "); + self.print_name_type(name, TypeKind::List); + self.output.str(" = "); + self.output.ty("list", TypeKind::BuiltIn); + self.output.str("<"); + self.print_type_name(resolve, ty)?; + self.output.str(&format!(", {elements}")); + self.output.str(">"); + self.output.semicolon(); + return Ok(()); + } + + Ok(()) + } + fn declare_stream( &mut self, resolve: &Resolve, diff --git a/crates/wit-encoder/src/from_parser.rs b/crates/wit-encoder/src/from_parser.rs index e8381d9b68..c07da9c6af 100644 --- a/crates/wit-encoder/src/from_parser.rs +++ b/crates/wit-encoder/src/from_parser.rs @@ -243,6 +243,10 @@ impl<'a> Converter<'a> { let output = Type::list(self.convert_type(ty)); TypeDefKind::Type(output) } + wit_parser::TypeDefKind::FixedSizeList(ty, size) => { + let output = Type::fixed_size_list(self.convert_type(ty), *size); + TypeDefKind::Type(output) + } wit_parser::TypeDefKind::Handle(handle) => { let output = self.handle_to_type(handle); TypeDefKind::Type(output) @@ -304,6 +308,9 @@ impl<'a> Converter<'a> { wit_parser::TypeDefKind::List(type_) => { Type::list(self.convert_type(type_)) } + wit_parser::TypeDefKind::FixedSizeList(type_, size) => { + Type::fixed_size_list(self.convert_type(type_), *size) + } wit_parser::TypeDefKind::Handle(handle) => self.handle_to_type(handle), wit_parser::TypeDefKind::Future(type_) => { Type::future(self.convert_option_type(type_)) diff --git a/crates/wit-encoder/src/ty.rs b/crates/wit-encoder/src/ty.rs index 75b7e7c141..18513b53f1 100644 --- a/crates/wit-encoder/src/ty.rs +++ b/crates/wit-encoder/src/ty.rs @@ -26,6 +26,7 @@ pub enum Type { Option(Box), Result(Box), List(Box), + FixedSizeList(Box, u32), Tuple(Tuple), Future(Option>), Stream(Option>), @@ -58,6 +59,9 @@ impl Type { pub fn list(type_: Type) -> Self { Type::List(Box::new(type_)) } + pub fn fixed_size_list(type_: Type, size: u32) -> Self { + Type::FixedSizeList(Box::new(type_), size) + } pub fn tuple(types: impl IntoIterator) -> Self { Type::Tuple(Tuple { types: types.into_iter().collect(), @@ -116,6 +120,9 @@ impl Display for Type { Type::List(type_) => { write!(f, "list<{type_}>") } + Type::FixedSizeList(type_, size) => { + write!(f, "list<{type_}, {size}>") + } Type::Tuple(tuple) => tuple.fmt(f), Type::Future(None) => { write!(f, "future") diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs index 675feeb48d..de1bb745a6 100644 --- a/crates/wit-parser/src/abi.rs +++ b/crates/wit-parser/src/abi.rs @@ -133,6 +133,9 @@ pub enum AbiVariant { } impl Resolve { + const MAX_FLAT_PARAMS: usize = 16; + const MAX_FLAT_RESULTS: usize = 1; + /// Get the WebAssembly type signature for this interface function /// /// The first entry returned is the list of parameters and the second entry @@ -147,16 +150,13 @@ impl Resolve { }; } - const MAX_FLAT_PARAMS: usize = 16; - const MAX_FLAT_RESULTS: usize = 1; - let mut params = Vec::new(); let mut indirect_params = false; for (_, param) in func.params.iter() { self.push_flat(param, &mut params); } - if params.len() > MAX_FLAT_PARAMS { + if params.len() > Self::MAX_FLAT_PARAMS { params.truncate(0); params.push(WasmType::Pointer); indirect_params = true; @@ -212,7 +212,7 @@ impl Resolve { // would have multiple results then instead truncate it. Imports take a // return pointer to write into and exports return a pointer they wrote // into. - if results.len() > MAX_FLAT_RESULTS { + if results.len() > Self::MAX_FLAT_RESULTS { retptr = true; results.truncate(0); match variant { @@ -288,6 +288,15 @@ impl Resolve { result.push(WasmType::Length); } + TypeDefKind::FixedSizeList(ty, size) => { + for _ in 0..usize::try_from(*size) + .unwrap_or(Self::MAX_FLAT_PARAMS) + .min(Self::MAX_FLAT_PARAMS) + { + self.push_flat(ty, result); + } + } + TypeDefKind::Variant(v) => { result.push(v.tag().into()); self.push_flat_variants(v.cases.iter().map(|c| c.ty.as_ref()), result); diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs index 34556c3b15..41186ab39a 100644 --- a/crates/wit-parser/src/ast.rs +++ b/crates/wit-parser/src/ast.rs @@ -754,6 +754,7 @@ enum Type<'a> { String(Span), Name(Id<'a>), List(List<'a>), + FixedSizeList(FixedSizeList<'a>), Handle(Handle<'a>), Resource(Resource<'a>), Record(Record<'a>), @@ -905,6 +906,12 @@ struct List<'a> { ty: Box>, } +struct FixedSizeList<'a> { + span: Span, + ty: Box>, + size: u32, +} + struct Future<'a> { span: Span, ty: Option>>, @@ -1363,14 +1370,34 @@ impl<'a> Type<'a> { Some((span, Token::String_)) => Ok(Type::String(span)), // list + // list Some((span, Token::List)) => { tokens.expect(Token::LessThan)?; let ty = Type::parse(tokens)?; + let size = if tokens.eat(Token::Comma)? { + let number = tokens.next()?; + if let Some((span, Token::Integer)) = number { + let size: u32 = tokens.get_span(span).parse()?; + Some(size) + } else { + return Err(err_expected(tokens, "fixed size", number).into()); + } + } else { + None + }; tokens.expect(Token::GreaterThan)?; - Ok(Type::List(List { - span, - ty: Box::new(ty), - })) + if let Some(size) = size { + Ok(Type::FixedSizeList(FixedSizeList { + span, + ty: Box::new(ty), + size, + })) + } else { + Ok(Type::List(List { + span, + ty: Box::new(ty), + })) + } } // option @@ -1483,6 +1510,7 @@ impl<'a> Type<'a> { | Type::ErrorContext(span) => *span, Type::Name(id) => id.span, Type::List(l) => l.span, + Type::FixedSizeList(l) => l.span, Type::Handle(h) => h.span(), Type::Resource(r) => r.span, Type::Record(r) => r.span, diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs index e1b7016bc8..0e626d8751 100644 --- a/crates/wit-parser/src/ast/resolve.rs +++ b/crates/wit-parser/src/ast/resolve.rs @@ -92,6 +92,7 @@ enum Key { Tuple(Vec), Enum(Vec), List(Type), + FixedSizeList(Type, u32), Option(Type), Result(Option, Option), Future(Option), @@ -1174,6 +1175,10 @@ impl<'a> Resolver<'a> { let ty = self.resolve_type(&list.ty, stability)?; TypeDefKind::List(ty) } + ast::Type::FixedSizeList(list) => { + let ty = self.resolve_type(&list.ty, stability)?; + TypeDefKind::FixedSizeList(ty, list.size) + } ast::Type::Handle(handle) => TypeDefKind::Handle(match handle { ast::Handle::Own { resource } => Handle::Own(self.validate_resource(resource)?), ast::Handle::Borrow { resource } => { @@ -1351,7 +1356,9 @@ impl<'a> Resolver<'a> { find_in_type(types, Type::Id(*id)) } TypeDefKind::Tuple(t) => t.types.iter().find_map(|ty| find_in_type(types, *ty)), - TypeDefKind::List(ty) | TypeDefKind::Option(ty) => find_in_type(types, *ty), + TypeDefKind::List(ty) + | TypeDefKind::FixedSizeList(ty, _) + | TypeDefKind::Option(ty) => find_in_type(types, *ty), TypeDefKind::Future(ty) | TypeDefKind::Stream(ty) => { ty.as_ref().and_then(|ty| find_in_type(types, *ty)) } @@ -1443,6 +1450,7 @@ impl<'a> Resolver<'a> { Key::Enum(r.cases.iter().map(|f| f.name.clone()).collect::>()) } TypeDefKind::List(ty) => Key::List(*ty), + TypeDefKind::FixedSizeList(ty, size) => Key::FixedSizeList(*ty, *size), TypeDefKind::Option(t) => Key::Option(*t), TypeDefKind::Result(r) => Key::Result(r.ok, r.err), TypeDefKind::Future(ty) => Key::Future(*ty), @@ -1678,9 +1686,9 @@ fn collect_deps<'a>(ty: &ast::Type<'a>, deps: &mut Vec>) { } } } - ast::Type::Option(ast::Option_ { ty, .. }) | ast::Type::List(ast::List { ty, .. }) => { - collect_deps(ty, deps) - } + ast::Type::Option(ast::Option_ { ty, .. }) + | ast::Type::List(ast::List { ty, .. }) + | ast::Type::FixedSizeList(ast::FixedSizeList { ty, .. }) => collect_deps(ty, deps), ast::Type::Result(r) => { if let Some(ty) = &r.ok { collect_deps(ty, deps); diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs index 26c7e93edd..7dce0d1cb1 100644 --- a/crates/wit-parser/src/decoding.rs +++ b/crates/wit-parser/src/decoding.rs @@ -1254,6 +1254,7 @@ impl WitPackageDecoder<'_> { match &kind { TypeDefKind::Type(_) | TypeDefKind::List(_) + | TypeDefKind::FixedSizeList(..) | TypeDefKind::Tuple(_) | TypeDefKind::Option(_) | TypeDefKind::Result(_) @@ -1294,6 +1295,11 @@ impl WitPackageDecoder<'_> { Ok(TypeDefKind::List(t)) } + ComponentDefinedType::FixedSizeList(t, size) => { + let t = self.convert_valtype(t)?; + Ok(TypeDefKind::FixedSizeList(t, *size)) + } + ComponentDefinedType::Tuple(t) => { let types = t .types @@ -1586,6 +1592,15 @@ impl Registrar<'_> { self.valtype(t, ty) } + ComponentDefinedType::FixedSizeList(t, elements) => { + let ty = match &self.resolve.types[id].kind { + TypeDefKind::FixedSizeList(r, elements2) if elements2 == elements => r, + TypeDefKind::Type(Type::Id(_)) => return Ok(()), + _ => bail!("expected a fixed size {elements} list"), + }; + self.valtype(t, ty) + } + ComponentDefinedType::Tuple(t) => { let ty = match &self.resolve.types[id].kind { TypeDefKind::Tuple(r) => r, diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs index d8551db1d7..5f8aa6c9f4 100644 --- a/crates/wit-parser/src/lib.rs +++ b/crates/wit-parser/src/lib.rs @@ -569,6 +569,7 @@ pub enum TypeDefKind { Option(Type), Result(Result_), List(Type), + FixedSizeList(Type, u32), Future(Option), Stream(Option), Type(Type), @@ -597,6 +598,7 @@ impl TypeDefKind { TypeDefKind::Option(_) => "option", TypeDefKind::Result(_) => "result", TypeDefKind::List(_) => "list", + TypeDefKind::FixedSizeList(..) => "fixed size list", TypeDefKind::Future(_) => "future", TypeDefKind::Stream(_) => "stream", TypeDefKind::Type(_) => "type", @@ -1153,7 +1155,10 @@ fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec { + TypeDefKind::Option(ty) + | TypeDefKind::List(ty) + | TypeDefKind::FixedSizeList(ty, ..) + | TypeDefKind::Type(ty) => { find_futures_and_streams(resolve, *ty, results); } TypeDefKind::Result(r) => { diff --git a/crates/wit-parser/src/live.rs b/crates/wit-parser/src/live.rs index 27a769b095..48637f5444 100644 --- a/crates/wit-parser/src/live.rs +++ b/crates/wit-parser/src/live.rs @@ -132,6 +132,7 @@ pub trait TypeIdVisitor { match &ty.kind { TypeDefKind::Type(t) | TypeDefKind::List(t) + | TypeDefKind::FixedSizeList(t, ..) | TypeDefKind::Option(t) | TypeDefKind::Future(Some(t)) | TypeDefKind::Stream(Some(t)) => self.visit_type(resolve, t), diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs index 8950dcd163..b258ccb9b4 100644 --- a/crates/wit-parser/src/resolve.rs +++ b/crates/wit-parser/src/resolve.rs @@ -573,7 +573,7 @@ package {name} is defined in two different locations:\n\ | TypeDefKind::Result(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => false, - TypeDefKind::Type(t) => self.all_bits_valid(t), + TypeDefKind::Type(t) | TypeDefKind::FixedSizeList(t, ..) => self.all_bits_valid(t), TypeDefKind::Handle(h) => match h { crate::Handle::Own(_) => true, @@ -3135,7 +3135,7 @@ impl Remap { } } } - Option(t) | List(t) | Future(Some(t)) | Stream(Some(t)) => { + Option(t) | List(t, ..) | FixedSizeList(t, ..) | Future(Some(t)) | Stream(Some(t)) => { self.update_ty(resolve, t, span)? } Result(r) => { @@ -3522,6 +3522,7 @@ impl Remap { TypeDefKind::Tuple(t) => t.types.iter().any(|t| self.type_has_borrow(resolve, t)), TypeDefKind::Enum(_) => false, TypeDefKind::List(ty) + | TypeDefKind::FixedSizeList(ty, ..) | TypeDefKind::Future(Some(ty)) | TypeDefKind::Stream(Some(ty)) | TypeDefKind::Option(ty) => self.type_has_borrow(resolve, ty), diff --git a/crates/wit-parser/src/resolve/clone.rs b/crates/wit-parser/src/resolve/clone.rs index 5554826fa9..32bc5bc299 100644 --- a/crates/wit-parser/src/resolve/clone.rs +++ b/crates/wit-parser/src/resolve/clone.rs @@ -109,7 +109,9 @@ impl<'a> Cloner<'a> { TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => { self.type_id(ty); } - TypeDefKind::Option(ty) | TypeDefKind::List(ty) => { + TypeDefKind::Option(ty) + | TypeDefKind::List(ty) + | TypeDefKind::FixedSizeList(ty, ..) => { self.ty(ty); } TypeDefKind::Tuple(list) => { diff --git a/crates/wit-parser/src/sizealign.rs b/crates/wit-parser/src/sizealign.rs index 5e0f0e0ddb..a9b96dc008 100644 --- a/crates/wit-parser/src/sizealign.rs +++ b/crates/wit-parser/src/sizealign.rs @@ -260,6 +260,17 @@ impl SizeAlign { fn calculate(&self, ty: &TypeDef) -> ElementInfo { match &ty.kind { TypeDefKind::Type(t) => ElementInfo::new(self.size(t), self.align(t)), + TypeDefKind::FixedSizeList(t, size) => { + let field_align = self.align(t); + let field_size = self.size(t); + ElementInfo::new( + ArchitectureSize::new( + field_size.bytes.checked_mul(*size as usize).unwrap(), + field_size.pointers.checked_mul(*size as usize).unwrap(), + ), + field_align, + ) + } TypeDefKind::List(_) => { ElementInfo::new(ArchitectureSize::new(0, 2), Alignment::Pointer) } diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit b/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit new file mode 100644 index 0000000000..4313c54958 --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/bad-list2.wit @@ -0,0 +1,7 @@ +// parse-fail + +interface foo { + type z = list tests/ui/parse-fail/bad-list2.wit:6:3 + | + 6 | type y = u32 + | ^--- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit new file mode 100644 index 0000000000..1f0238ea03 --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit @@ -0,0 +1,7 @@ +// parse-fail + +interface foo { + type a = list + + type y = u32 +} diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit.result new file mode 100644 index 0000000000..4f48238f89 --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/bad-list3.wit.result @@ -0,0 +1,5 @@ +expected fixed size, found '>' + --> tests/ui/parse-fail/bad-list3.wit:4:21 + | + 4 | type a = list + | ^ \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit new file mode 100644 index 0000000000..14fa344da0 --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit @@ -0,0 +1,7 @@ +// parse-fail + +interface foo { + type b = list + + type y = u32 +} diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit.result new file mode 100644 index 0000000000..c2aae207fa --- /dev/null +++ b/crates/wit-parser/tests/ui/parse-fail/bad-list4.wit.result @@ -0,0 +1,5 @@ +expected fixed size, found keyword `u32` + --> tests/ui/parse-fail/bad-list4.wit:4:22 + | + 4 | type b = list + | ^-- \ No newline at end of file diff --git a/crates/wit-parser/tests/ui/types.wit b/crates/wit-parser/tests/ui/types.wit index d67939799d..5a644f305c 100644 --- a/crates/wit-parser/tests/ui/types.wit +++ b/crates/wit-parser/tests/ui/types.wit @@ -50,6 +50,8 @@ interface types { type t48 = stream; type t49 = future; type t50 = future; + type t51a = list; + type t51b = list, 2>; /// type order doesn't matter type foo = bar; diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json index 1b5e70e51d..a8dfbc8136 100644 --- a/crates/wit-parser/tests/ui/types.wit.json +++ b/crates/wit-parser/tests/ui/types.wit.json @@ -53,8 +53,10 @@ "t48": 49, "t49": 50, "t50": 51, - "bar": 52, - "foo": 53 + "t51a": 52, + "t51b": 54, + "bar": 55, + "foo": 56 }, "functions": {}, "package": 0 @@ -698,6 +700,40 @@ "interface": 0 } }, + { + "name": "t51a", + "kind": { + "fixed-size-list": [ + "u32", + 4 + ] + }, + "owner": { + "interface": 0 + } + }, + { + "name": null, + "kind": { + "fixed-size-list": [ + "u32", + 4 + ] + }, + "owner": null + }, + { + "name": "t51b", + "kind": { + "fixed-size-list": [ + 53, + 2 + ] + }, + "owner": { + "interface": 0 + } + }, { "name": "bar", "kind": { @@ -710,7 +746,7 @@ { "name": "foo", "kind": { - "type": 52 + "type": 55 }, "owner": { "interface": 0 diff --git a/crates/wit-smith/src/generate.rs b/crates/wit-smith/src/generate.rs index d02edc419a..6a2204b3e7 100644 --- a/crates/wit-smith/src/generate.rs +++ b/crates/wit-smith/src/generate.rs @@ -722,6 +722,7 @@ impl<'a> InterfaceGenerator<'a> { Option, Result, List, + FixedSizeList, Stream, Future, ErrorContext, @@ -803,6 +804,16 @@ impl<'a> InterfaceGenerator<'a> { self.gen_type(u, fuel, dst)?; dst.push_str(">"); } + Kind::FixedSizeList => { + *fuel = match fuel.checked_sub(1) { + Some(fuel) => fuel, + None => continue, + }; + let elements = u.int_in_range(1..=self.config.max_type_parts as u32)?; + dst.push_str("list<"); + self.gen_type(u, fuel, dst)?; + dst.push_str(&format!(", {elements}>")); + } Kind::Result => { *fuel = match fuel.checked_sub(2) { Some(fuel) => fuel, diff --git a/tests/cli/component-model/fixed-size-list.wast b/tests/cli/component-model/fixed-size-list.wast new file mode 100644 index 0000000000..63b8943f11 --- /dev/null +++ b/tests/cli/component-model/fixed-size-list.wast @@ -0,0 +1,79 @@ +;; RUN: wast % --assert default --snapshot tests/snapshots -f cm-fixed-size-list + +(component + (core module $m + (memory (export "memory") 1) + (func (export "ret-list") (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "ret-list") (result (list u32 4)) + (canon lift (core func $i "ret-list") (memory $i "memory")) + ) +) + +(component + (core module $m + (func (export "param-list") (param i32 i32 i32 i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "param-list") (param "l" (list u32 4)) + (canon lift (core func $i "param-list")) + ) +) + +(assert_invalid + (component + (core module $m + (memory (export "memory") 1) + (func (export "ret-list") (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "ret-list") (result (list u32 0)) + (canon lift (core func $i "ret-list") (memory $i "memory")) + ) + ) + "Fixed size lists must have more than zero elements (at offset 0x54)" +) + +(assert_malformed + (component quote + "(core module $m" + "(memory (export \"memory\") 1)" + "(func (export \"ret-list\") (result i32) unreachable)" + ")" + "(core instance $i (instantiate $m))" + + "(func (export \"ret-list\") (result (list u32 10000000000))" + "(canon lift (core func $i \"ret-list\") (memory $i \"memory\"))" + ")" + ) + "invalid u32 number: constant out of range" +) + +(assert_invalid + (component + (import "y" (component $c + (type $t (list s32 10)) + (import "x" (type (eq $t))) + )) + + (type $x (list u32 9)) + (instance (instantiate $c (with "x" (type $x)))) + ) + "type mismatch for import `x`") + + +(assert_invalid + (component + (import "y" (component $c + (type $t (list s32 10)) + (import "x" (type (eq $t))) + )) + + (type $x (list u32)) + (instance (instantiate $c (with "x" (type $x)))) + ) + "type mismatch for import `x`") diff --git a/tests/cli/missing-features/component-model/fixed-size-list.wast b/tests/cli/missing-features/component-model/fixed-size-list.wast new file mode 100644 index 0000000000..b6a92ada29 --- /dev/null +++ b/tests/cli/missing-features/component-model/fixed-size-list.wast @@ -0,0 +1,30 @@ +;; RUN: wast % --assert default --snapshot tests/snapshots + +(assert_invalid + (component + (core module $m + (memory (export "memory") 1) + (func (export "ret-list") (result i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "ret-list") (result (list u32 4)) + (canon lift (core func $i "ret-list") (memory $i "memory")) + ) + ) + "Fixed size lists require the component model fixed size list feature (at offset 0x54)" +) + +(assert_invalid + (component + (core module $m + (func (export "param-list") (param i32 i32 i32 i32) unreachable) + ) + (core instance $i (instantiate $m)) + + (func (export "param-list") (param "l" (list u32 4)) + (canon lift (core func $i "param-list")) + ) + ) + "Fixed size lists require the component model fixed size list feature" +) diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr index 1c6e4539c4..6c52f3ea8b 100644 --- a/tests/cli/validate-unknown-features.wat.stderr +++ b/tests/cli/validate-unknown-features.wat.stderr @@ -1,4 +1,4 @@ error: invalid value 'unknown' for '--features ': unknown feature `unknown` -Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, legacy-exceptions, gc-types, stack-switching, wide-arithmetic, cm-values, cm-nested-names, cm-async, cm-async-stackful, cm-async-builtins, cm-error-context, mvp, wasm1, wasm2, wasm3, all +Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, legacy-exceptions, gc-types, stack-switching, wide-arithmetic, cm-values, cm-nested-names, cm-async, cm-async-stackful, cm-async-builtins, cm-error-context, cm-fixed-size-list, mvp, wasm1, wasm2, wasm3, all For more information, try '--help'. diff --git a/tests/snapshots/cli/component-model/fixed-size-list.wast.json b/tests/snapshots/cli/component-model/fixed-size-list.wast.json new file mode 100644 index 0000000000..2d7c8c444c --- /dev/null +++ b/tests/snapshots/cli/component-model/fixed-size-list.wast.json @@ -0,0 +1,45 @@ +{ + "source_filename": "tests/cli/component-model/fixed-size-list.wast", + "commands": [ + { + "type": "module", + "line": 3, + "filename": "fixed-size-list.0.wasm", + "module_type": "binary" + }, + { + "type": "module", + "line": 15, + "filename": "fixed-size-list.1.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 27, + "filename": "fixed-size-list.2.wasm", + "module_type": "binary", + "text": "Fixed size lists must have more than zero elements (at offset 0x54)" + }, + { + "type": "assert_malformed", + "line": 42, + "filename": "fixed-size-list.3.wat", + "module_type": "text", + "text": "invalid u32 number: constant out of range" + }, + { + "type": "assert_invalid", + "line": 57, + "filename": "fixed-size-list.4.wasm", + "module_type": "binary", + "text": "type mismatch for import `x`" + }, + { + "type": "assert_invalid", + "line": 70, + "filename": "fixed-size-list.5.wasm", + "module_type": "binary", + "text": "type mismatch for import `x`" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/cli/component-model/fixed-size-list.wast/0.print b/tests/snapshots/cli/component-model/fixed-size-list.wast/0.print new file mode 100644 index 0000000000..79848d6748 --- /dev/null +++ b/tests/snapshots/cli/component-model/fixed-size-list.wast/0.print @@ -0,0 +1,18 @@ +(component + (core module $m (;0;) + (type (;0;) (func (result i32))) + (memory (;0;) 1) + (export "memory" (memory 0)) + (export "ret-list" (func 0)) + (func (;0;) (type 0) (result i32) + unreachable + ) + ) + (core instance $i (;0;) (instantiate $m)) + (type (;0;) (list u32 4)) + (type (;1;) (func (result 0))) + (alias core export $i "ret-list" (core func (;0;))) + (alias core export $i "memory" (core memory (;0;))) + (func (;0;) (type 1) (canon lift (core func 0) (memory 0))) + (export (;1;) "ret-list" (func 0)) +) diff --git a/tests/snapshots/cli/component-model/fixed-size-list.wast/1.print b/tests/snapshots/cli/component-model/fixed-size-list.wast/1.print new file mode 100644 index 0000000000..ee75456d39 --- /dev/null +++ b/tests/snapshots/cli/component-model/fixed-size-list.wast/1.print @@ -0,0 +1,15 @@ +(component + (core module $m (;0;) + (type (;0;) (func (param i32 i32 i32 i32))) + (export "param-list" (func 0)) + (func (;0;) (type 0) (param i32 i32 i32 i32) + unreachable + ) + ) + (core instance $i (;0;) (instantiate $m)) + (type (;0;) (list u32 4)) + (type (;1;) (func (param "l" 0))) + (alias core export $i "param-list" (core func (;0;))) + (func (;0;) (type 1) (canon lift (core func 0))) + (export (;1;) "param-list" (func 0)) +) diff --git a/tests/snapshots/cli/missing-features/component-model/fixed-size-list.wast.json b/tests/snapshots/cli/missing-features/component-model/fixed-size-list.wast.json new file mode 100644 index 0000000000..d91233361d --- /dev/null +++ b/tests/snapshots/cli/missing-features/component-model/fixed-size-list.wast.json @@ -0,0 +1,19 @@ +{ + "source_filename": "tests/cli/missing-features/component-model/fixed-size-list.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 4, + "filename": "fixed-size-list.0.wasm", + "module_type": "binary", + "text": "Fixed size lists require the component model fixed size list feature (at offset 0x54)" + }, + { + "type": "assert_invalid", + "line": 19, + "filename": "fixed-size-list.1.wasm", + "module_type": "binary", + "text": "Fixed size lists require the component model fixed size list feature" + } + ] +} \ No newline at end of file