diff --git a/Cargo.lock b/Cargo.lock index 3794317ec8f0..0f243840061b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -211,7 +211,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.12.1", + "itertools 0.13.0", "log", "prettyplease", "proc-macro2", @@ -2107,15 +2107,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" diff --git a/crates/cli-flags/src/lib.rs b/crates/cli-flags/src/lib.rs index ada037e26343..37dea7f26ac7 100644 --- a/crates/cli-flags/src/lib.rs +++ b/crates/cli-flags/src/lib.rs @@ -394,6 +394,9 @@ wasmtime_option_group! { pub exceptions: Option, /// DEPRECATED: Configure support for the legacy exceptions proposal. pub legacy_exceptions: Option, + /// Component model support for fixed size lists: this corresponds + /// to the 🔧 emoji in the component model specification + pub component_model_fixed_size_list: Option, } enum Wasm { @@ -1038,6 +1041,7 @@ impl CommonOptions { ("component-model-async", component_model_async, wasm_component_model_async) ("component-model-async", component_model_async_builtins, wasm_component_model_async_builtins) ("component-model-async", component_model_async_stackful, wasm_component_model_async_stackful) + ("component-model", component_model_fixed_size_list, wasm_component_model_fixed_size_lists) ("component-model", component_model_error_context, wasm_component_model_error_context) ("threads", threads, wasm_threads) ("gc", gc, wasm_gc) diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs index c6ea97ae4c6f..3e772519d8b5 100644 --- a/crates/environ/src/component/types.rs +++ b/crates/environ/src/component/types.rs @@ -89,6 +89,8 @@ indices! { pub struct TypeResultIndex(u32); /// Index pointing to a list type in the component model. pub struct TypeListIndex(u32); + /// Index pointing to a fixed size list type in the component model. + pub struct TypeFixedSizeListIndex(u32); /// Index pointing to a future type in the component model. pub struct TypeFutureIndex(u32); @@ -285,6 +287,7 @@ pub struct ComponentTypes { pub(super) stream_tables: PrimaryMap, pub(super) error_context_tables: PrimaryMap, + pub(super) fixed_size_lists: PrimaryMap, } impl TypeTrace for ComponentTypes { @@ -358,6 +361,7 @@ impl ComponentTypes { InterfaceType::Enum(i) => &self[*i].abi, InterfaceType::Option(i) => &self[*i].abi, InterfaceType::Result(i) => &self[*i].abi, + InterfaceType::FixedSizeList(i) => &self[*i].abi, } } @@ -407,6 +411,7 @@ impl_index! { impl Index for ComponentTypes { TypeFutureTable => future_tables } impl Index for ComponentTypes { TypeStreamTable => stream_tables } impl Index for ComponentTypes { TypeErrorContextTable => error_context_tables } + impl Index for ComponentTypes { TypeFixedSizeList => fixed_size_lists } } // Additionally forward anything that can index `ModuleTypes` to `ModuleTypes` @@ -582,6 +587,7 @@ pub enum InterfaceType { Future(TypeFutureTableIndex), Stream(TypeStreamTableIndex), ErrorContext(TypeComponentLocalErrorContextTableIndex), + FixedSizeList(TypeFixedSizeListIndex), } /// Bye information about a type in the canonical ABI, with metadata for both @@ -1121,6 +1127,17 @@ pub struct TypeList { pub element: InterfaceType, } +/// Shape of a "fixed size list" interface type. +#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)] +pub struct TypeFixedSizeList { + /// The element type of the list. + pub element: InterfaceType, + /// The fixed length of the list. + pub size: u32, + /// Byte information about this type in the canonical ABI. + pub abi: CanonicalAbiInfo, +} + /// Maximum number of flat types, for either params or results. pub const MAX_FLAT_TYPES: usize = if MAX_FLAT_PARAMS > MAX_FLAT_RESULTS { MAX_FLAT_PARAMS diff --git a/crates/environ/src/component/types_builder.rs b/crates/environ/src/component/types_builder.rs index b891612269e0..6aa3b051cb9c 100644 --- a/crates/environ/src/component/types_builder.rs +++ b/crates/environ/src/component/types_builder.rs @@ -50,6 +50,7 @@ pub struct ComponentTypesBuilder { future_tables: HashMap, stream_tables: HashMap, error_context_tables: HashMap, + fixed_size_lists: HashMap, component_types: ComponentTypes, module_types: ModuleTypesBuilder, @@ -111,6 +112,7 @@ impl ComponentTypesBuilder { component_types: ComponentTypes::default(), type_info: TypeInformationCache::default(), resources: ResourcesBuilder::default(), + fixed_size_lists: HashMap::default(), } } @@ -418,8 +420,8 @@ impl ComponentTypesBuilder { ComponentDefinedType::Stream(ty) => { InterfaceType::Stream(self.stream_table_type(types, ty)?) } - ComponentDefinedType::FixedSizeList(..) => { - bail!("support not implemented for fixed-size-lists"); + ComponentDefinedType::FixedSizeList(ty, size) => { + InterfaceType::FixedSizeList(self.fixed_size_list_type(types, ty, *size)?) } }; let info = self.type_information(&ret); @@ -534,6 +536,27 @@ impl ComponentTypesBuilder { self.add_tuple_type(TypeTuple { types, abi }) } + fn fixed_size_list_type( + &mut self, + types: TypesRef<'_>, + ty: &ComponentValType, + size: u32, + ) -> Result { + assert_eq!(types.id(), self.module_types.validator_id()); + let element = self.valtype(types, ty)?; + Ok(self.new_fixed_size_list_type(element, size)) + } + + pub(crate) fn new_fixed_size_list_type( + &mut self, + element: InterfaceType, + size: u32, + ) -> TypeFixedSizeListIndex { + let element_abi = self.component_types.canonical_abi(&element); + let abi = CanonicalAbiInfo::record((0..size).into_iter().map(|_| element_abi)); + self.add_fixed_size_list_type(TypeFixedSizeList { element, size, abi }) + } + fn flags_type(&mut self, flags: &IndexSet) -> TypeFlagsIndex { let flags = TypeFlags { names: flags.iter().map(|s| s.to_string()).collect(), @@ -650,6 +673,11 @@ impl ComponentTypesBuilder { intern_and_fill_flat_types!(self, tuples, ty) } + /// Interns a new tuple type within this type information. + pub fn add_fixed_size_list_type(&mut self, ty: TypeFixedSizeList) -> TypeFixedSizeListIndex { + intern_and_fill_flat_types!(self, fixed_size_lists, ty) + } + /// Interns a new variant type within this type information. pub fn add_variant_type(&mut self, ty: TypeVariant) -> TypeVariantIndex { intern_and_fill_flat_types!(self, variants, ty) @@ -785,6 +813,7 @@ impl ComponentTypesBuilder { InterfaceType::Enum(i) => &self.type_info.enums[*i], InterfaceType::Option(i) => &self.type_info.options[*i], InterfaceType::Result(i) => &self.type_info.results[*i], + InterfaceType::FixedSizeList(i) => &self.type_info.fixed_size_lists[*i], } } } @@ -894,6 +923,7 @@ struct TypeInformationCache { options: PrimaryMap, results: PrimaryMap, lists: PrimaryMap, + fixed_size_lists: PrimaryMap, } struct TypeInformation { @@ -1043,6 +1073,14 @@ impl TypeInformation { self.build_record(ty.types.iter().map(|t| types.type_information(t))); } + fn fixed_size_lists(&mut self, types: &ComponentTypesBuilder, ty: &TypeFixedSizeList) { + self.build_record( + (0..ty.size) + .into_iter() + .map(|_| types.type_information(&ty.element)), + ); + } + fn enums(&mut self, _types: &ComponentTypesBuilder, _ty: &TypeEnum) { self.depth = 1; self.flat.push(FlatType::I32, FlatType::I32); diff --git a/crates/environ/src/fact/trampoline.rs b/crates/environ/src/fact/trampoline.rs index ba0b7d7c890e..0b1de64488be 100644 --- a/crates/environ/src/fact/trampoline.rs +++ b/crates/environ/src/fact/trampoline.rs @@ -19,9 +19,9 @@ use crate::component::{ CanonicalAbiInfo, ComponentTypesBuilder, FLAG_MAY_ENTER, FLAG_MAY_LEAVE, FixedEncoding as FE, FlatType, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS, PREPARE_ASYNC_NO_RESULT, PREPARE_ASYNC_WITH_RESULT, StringEncoding, Transcode, TypeComponentLocalErrorContextTableIndex, - TypeEnumIndex, TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, TypeOptionIndex, - TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, TypeStreamTableIndex, TypeTupleIndex, - TypeVariantIndex, VariantInfo, + TypeEnumIndex, TypeFixedSizeListIndex, TypeFlagsIndex, TypeFutureTableIndex, TypeListIndex, + TypeOptionIndex, TypeRecordIndex, TypeResourceTableIndex, TypeResultIndex, + TypeStreamTableIndex, TypeTupleIndex, TypeVariantIndex, VariantInfo, }; use crate::fact::signature::Signature; use crate::fact::transcode::Transcoder; @@ -1098,6 +1098,7 @@ impl<'a, 'b> Compiler<'a, 'b> { | InterfaceType::Future(_) | InterfaceType::Stream(_) | InterfaceType::ErrorContext(_) => 1, + InterfaceType::FixedSizeList(i) => self.types[*i].size as usize, }; match self.fuel.checked_sub(cost) { @@ -1137,6 +1138,9 @@ impl<'a, 'b> Compiler<'a, 'b> { InterfaceType::ErrorContext(t) => { self.translate_error_context(*t, src, dst_ty, dst) } + InterfaceType::FixedSizeList(t) => { + self.translate_fixed_size_list(*t, src, dst_ty, dst); + } } } @@ -2830,6 +2834,35 @@ impl<'a, 'b> Compiler<'a, 'b> { } } + fn translate_fixed_size_list( + &mut self, + src_ty: TypeFixedSizeListIndex, + src: &Source<'_>, + dst_ty: &InterfaceType, + dst: &Destination, + ) { + let src_ty = &self.types[src_ty]; + let dst_ty = match dst_ty { + InterfaceType::FixedSizeList(t) => &self.types[*t], + _ => panic!("expected a fixed size list"), + }; + + // TODO: subtyping + assert_eq!(src_ty.size, dst_ty.size); + + let srcs = src.record_field_srcs( + self.types, + (0..src_ty.size).into_iter().map(|_| src_ty.element), + ); + let dsts = dst.record_field_dsts( + self.types, + (0..dst_ty.size).into_iter().map(|_| dst_ty.element), + ); + for (src, dst) in srcs.zip(dsts) { + self.translate(&src_ty.element, &src, &dst_ty.element, &dst); + } + } + fn translate_variant( &mut self, src_ty: TypeVariantIndex, diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 8c33cc4816e4..2bc6043ce47c 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1159,6 +1159,16 @@ impl Config { self } + /// This corresponds to the 🔧 emoji in the component model specification. + /// + /// Please note that Wasmtime's support for this feature is _very_ + /// incomplete. + #[cfg(feature = "component-model")] + pub fn wasm_component_model_fixed_size_lists(&mut self, enable: bool) -> &mut Self { + self.wasm_feature(WasmFeatures::CM_FIXED_SIZE_LIST, enable); + self + } + /// This corresponds to the 📝 emoji in the component model specification. /// /// Please note that Wasmtime's support for this feature is _very_ diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index ab3972d7dfc0..f098deecc004 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -2873,6 +2873,7 @@ pub fn desc(ty: &InterfaceType) -> &'static str { InterfaceType::Future(_) => "future", InterfaceType::Stream(_) => "stream", InterfaceType::ErrorContext(_) => "error-context", + InterfaceType::FixedSizeList(_) => "list<_, N>", } } diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs index 5f4373dfc0df..a011481586c1 100644 --- a/crates/wasmtime/src/runtime/component/types.rs +++ b/crates/wasmtime/src/runtime/component/types.rs @@ -156,6 +156,7 @@ impl TypeChecker<'_> { (InterfaceType::Stream(_), _) => false, (InterfaceType::ErrorContext(_), InterfaceType::ErrorContext(_)) => true, (InterfaceType::ErrorContext(_), _) => false, + (InterfaceType::FixedSizeList(_), _) => todo!(), } } @@ -757,6 +758,7 @@ impl Type { InterfaceType::Future(index) => Type::Future(instance.future_type(*index)), InterfaceType::Stream(index) => Type::Stream(instance.stream_type(*index)), InterfaceType::ErrorContext(_) => Type::ErrorContext, + InterfaceType::FixedSizeList(_) => todo!(), } } diff --git a/crates/wasmtime/src/runtime/component/values.rs b/crates/wasmtime/src/runtime/component/values.rs index dbe129ca4370..333cacb7534d 100644 --- a/crates/wasmtime/src/runtime/component/values.rs +++ b/crates/wasmtime/src/runtime/component/values.rs @@ -215,6 +215,7 @@ impl Val { InterfaceType::ErrorContext(_) => { ErrorContext::linear_lift_from_flat(cx, ty, next(src))?.into_val() } + InterfaceType::FixedSizeList(_) => todo!(), }) } @@ -345,6 +346,7 @@ impl Val { InterfaceType::ErrorContext(_) => { ErrorContext::linear_lift_from_memory(cx, ty, bytes)?.into_val() } + InterfaceType::FixedSizeList(_) => todo!(), }) } @@ -503,6 +505,7 @@ impl Val { ) } (InterfaceType::ErrorContext(_), _) => unexpected(ty, self), + (InterfaceType::FixedSizeList(_), _) => todo!(), } } @@ -668,6 +671,7 @@ impl Val { ) } (InterfaceType::ErrorContext(_), _) => unexpected(ty, self), + (InterfaceType::FixedSizeList(_), _) => todo!(), } }