From 50f63d4866515909aef7509282a093576f32713f Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 9 Sep 2024 15:40:18 -0500 Subject: [PATCH] Share more core type infrastructure in components (#1765) This commit chiefly fixes the validator panic found in #1763. This is done by sharing more infrastructure when validating types between the core module validator and the component validator. This sharing is done by extracting a new trait and moving methods from the core module to the trait and then implementing the trait for both component and core contexts. When writing tests this additionally discovered that name resolution on core types wasn't happening within components. The fix there was the same as the core module validator which was to extract shared bits to a trait and then implement the trait in both locations. Closes #1763 --- crates/wasmparser/src/validator/component.rs | 48 ++-- crates/wasmparser/src/validator/core.rs | 207 ++--------------- .../src/validator/core/canonical.rs | 216 ++++++++++++++++-- crates/wast/src/component/binary.rs | 34 +-- crates/wast/src/component/resolve.rs | 12 +- crates/wast/src/core/resolve/mod.rs | 2 + crates/wast/src/core/resolve/names.rs | 117 ++++++---- tests/local/component-model/types.wast | 42 ++++ .../component-model/types.wast | 18 ++ .../local/component-model/types.wast.json | 19 ++ .../local/component-model/types.wast/42.print | 19 ++ .../component-model/types.wast.json | 26 +++ 12 files changed, 472 insertions(+), 288 deletions(-) create mode 100644 tests/local/missing-features/component-model/types.wast create mode 100644 tests/snapshots/local/component-model/types.wast/42.print create mode 100644 tests/snapshots/local/missing-features/component-model/types.wast.json diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 405a03c573..d29f896d74 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -2,7 +2,7 @@ use super::{ check_max, - core::Module, + core::{InternRecGroup, Module}, types::{ AliasableResourceId, ComponentCoreInstanceTypeId, ComponentDefinedTypeId, ComponentFuncType, ComponentFuncTypeId, ComponentInstanceType, ComponentInstanceTypeId, @@ -261,12 +261,18 @@ impl ComponentState { offset: usize, check_limit: bool, ) -> Result<()> { - let id = match ty { + let current = components.last_mut().unwrap(); + if check_limit { + check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; + } + match ty { crate::CoreType::Sub(sub) => { - let (_is_new, group_id) = - types.intern_canonical_rec_group(RecGroup::implicit(offset, sub)); - let id = types[group_id].start; - ComponentCoreTypeId::Sub(id) + current.canonicalize_and_intern_rec_group( + features, + types, + RecGroup::implicit(offset, sub), + offset, + )?; } crate::CoreType::Module(decls) => { let mod_ty = Self::create_module_type( @@ -276,16 +282,11 @@ impl ComponentState { types, offset, )?; - let id = types.push_ty(mod_ty); - ComponentCoreTypeId::Module(id) + let id = ComponentCoreTypeId::Module(types.push_ty(mod_ty)); + components.last_mut().unwrap().core_types.push(id); } - }; - - let current = components.last_mut().unwrap(); - if check_limit { - check_max(current.type_count(), 1, MAX_WASM_TYPES, "types", offset)?; } - current.core_types.push(id); + Ok(()) } @@ -3036,6 +3037,25 @@ impl ComponentState { } } +impl InternRecGroup for ComponentState { + fn add_type_id(&mut self, id: CoreTypeId) { + self.core_types.push(ComponentCoreTypeId::Sub(id)); + } + + fn type_id_at(&self, idx: u32, offset: usize) -> Result { + match self.core_type_at(idx, offset)? { + ComponentCoreTypeId::Sub(id) => Ok(id), + ComponentCoreTypeId::Module(_) => { + bail!(offset, "type index {idx} is a module type, not a sub type"); + } + } + } + + fn types_len(&self) -> u32 { + u32::try_from(self.core_types.len()).unwrap() + } +} + impl ComponentNameContext { /// Registers that the resource `id` is named `name` within this context. fn register(&mut self, name: &str, id: AliasableResourceId) { diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 88ec740b53..51ad09a7ae 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -2,18 +2,18 @@ //! mod canonical; +pub(crate) use canonical::InternRecGroup; -use self::{arc::MaybeOwned, canonical::canonicalize_and_intern_rec_group}; +use self::arc::MaybeOwned; use super::{ check_max, combine_type_sizes, operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations}, types::{CoreTypeId, EntityType, RecGroupId, TypeAlloc, TypeList}, }; use crate::{ - limits::*, validator::types::TypeIdentifier, BinaryReaderError, CompositeType, ConstExpr, Data, - DataKind, Element, ElementKind, ExternalKind, FuncType, Global, GlobalType, HeapType, - MemoryType, PackedIndex, RecGroup, RefType, Result, StorageType, SubType, Table, TableInit, - TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures, + limits::*, BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, + FuncType, Global, GlobalType, HeapType, MemoryType, RecGroup, RefType, Result, SubType, Table, + TableInit, TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures, WasmModuleResources, }; use crate::{prelude::*, CompositeInnerType}; @@ -552,21 +552,6 @@ pub(crate) struct Module { } impl Module { - /// Get the `CoreTypeId` of the type at the given packed index. - pub(crate) fn at_packed_index( - &self, - types: &TypeList, - rec_group: RecGroupId, - index: PackedIndex, - offset: usize, - ) -> Result { - match index.unpack() { - UnpackedIndex::Id(id) => Ok(id), - UnpackedIndex::Module(idx) => self.type_id_at(idx, offset), - UnpackedIndex::RecGroup(idx) => types.rec_group_local_id(rec_group, idx, offset), - } - } - pub fn add_types( &mut self, rec_group: RecGroup, @@ -575,14 +560,6 @@ impl Module { offset: usize, check_limit: bool, ) -> Result<()> { - debug_assert!(rec_group.is_explicit_rec_group() || rec_group.types().len() == 1); - if rec_group.is_explicit_rec_group() && !features.gc() { - bail!( - offset, - "rec group usage requires `gc` proposal to be enabled" - ); - } - if check_limit { check_max( self.types.len(), @@ -592,155 +569,7 @@ impl Module { offset, )?; } - - let (is_new, rec_group_id) = - canonicalize_and_intern_rec_group(features, types, self, rec_group, offset)?; - - let range = &types[rec_group_id]; - let start = range.start.index(); - let end = range.end.index(); - - for i in start..end { - let i = u32::try_from(i).unwrap(); - let id = CoreTypeId::from_index(i); - debug_assert!(types.get(id).is_some()); - self.types.push(id); - if is_new { - self.check_subtype(rec_group_id, id, features, types, offset)?; - } - } - - Ok(()) - } - - fn check_subtype( - &mut self, - rec_group: RecGroupId, - id: CoreTypeId, - features: &WasmFeatures, - types: &mut TypeAlloc, - offset: usize, - ) -> Result<()> { - let ty = &types[id]; - if !features.gc() && (!ty.is_final || ty.supertype_idx.is_some()) { - bail!(offset, "gc proposal must be enabled to use subtypes"); - } - - self.check_composite_type(&ty.composite_type, features, &types, offset)?; - - let depth = if let Some(supertype_index) = ty.supertype_idx { - debug_assert!(supertype_index.is_canonical()); - let sup_id = self.at_packed_index(types, rec_group, supertype_index, offset)?; - if types[sup_id].is_final { - bail!(offset, "sub type cannot have a final super type"); - } - if !types.matches(id, sup_id) { - bail!(offset, "sub type must match super type"); - } - let depth = types.get_subtyping_depth(sup_id) + 1; - if usize::from(depth) > crate::limits::MAX_WASM_SUBTYPING_DEPTH { - bail!( - offset, - "sub type hierarchy too deep: found depth {}, cannot exceed depth {}", - depth, - crate::limits::MAX_WASM_SUBTYPING_DEPTH, - ); - } - depth - } else { - 0 - }; - types.set_subtyping_depth(id, depth); - - Ok(()) - } - - fn check_composite_type( - &mut self, - ty: &CompositeType, - features: &WasmFeatures, - types: &TypeList, - offset: usize, - ) -> Result<()> { - let check = |ty: &ValType, shared: bool| { - features - .check_value_type(*ty) - .map_err(|e| BinaryReaderError::new(e, offset))?; - if shared && !types.valtype_is_shared(*ty) { - return Err(BinaryReaderError::new( - "shared composite type must contain shared types", - offset, - )); - // The other cases are fine: - // - both shared or unshared: good to go - // - the func type is unshared, `ty` is shared: though - // odd, we _can_ in fact use shared values in - // unshared composite types (e.g., functions). - } - Ok(()) - }; - if !features.shared_everything_threads() && ty.shared { - return Err(BinaryReaderError::new( - "shared composite types require the shared-everything-threads proposal", - offset, - )); - } - match &ty.inner { - CompositeInnerType::Func(t) => { - for vt in t.params().iter().chain(t.results()) { - check(vt, ty.shared)?; - } - if t.results().len() > 1 && !features.multi_value() { - return Err(BinaryReaderError::new( - "func type returns multiple values but the multi-value feature is not enabled", - offset, - )); - } - } - CompositeInnerType::Array(t) => { - if !features.gc() { - bail!( - offset, - "array indexed types not supported without the gc feature", - ); - } - if !features.gc_types() { - bail!( - offset, - "cannot define array types when gc types are disabled", - ); - } - match &t.0.element_type { - StorageType::I8 | StorageType::I16 => { - // Note: scalar types are always `shared`. - } - StorageType::Val(value_type) => check(value_type, ty.shared)?, - }; - } - CompositeInnerType::Struct(t) => { - if !features.gc() { - bail!( - offset, - "struct indexed types not supported without the gc feature", - ); - } - if !features.gc_types() { - bail!( - offset, - "cannot define struct types when gc types are disabled", - ); - } - for ft in t.fields.iter() { - match &ft.element_type { - StorageType::I8 | StorageType::I16 => { - // Note: scalar types are always `shared`. - } - StorageType::Val(value_type) => check(value_type, ty.shared)?, - } - } - } - } - Ok(()) + self.canonicalize_and_intern_rec_group(features, types, rec_group, offset) } pub fn add_import( @@ -859,13 +688,6 @@ impl Module { Ok(()) } - pub fn type_id_at(&self, idx: u32, offset: usize) -> Result { - self.types - .get(idx as usize) - .copied() - .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) - } - fn sub_type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a SubType> { let id = self.type_id_at(idx, offset)?; Ok(&types[id]) @@ -1276,6 +1098,23 @@ impl Module { } } +impl InternRecGroup for Module { + fn add_type_id(&mut self, id: CoreTypeId) { + self.types.push(id); + } + + fn type_id_at(&self, idx: u32, offset: usize) -> Result { + self.types + .get(idx as usize) + .copied() + .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) + } + + fn types_len(&self) -> u32 { + u32::try_from(self.types.len()).unwrap() + } +} + impl Default for Module { fn default() -> Self { Self { diff --git a/crates/wasmparser/src/validator/core/canonical.rs b/crates/wasmparser/src/validator/core/canonical.rs index 01358d9986..fafb35d338 100644 --- a/crates/wasmparser/src/validator/core/canonical.rs +++ b/crates/wasmparser/src/validator/core/canonical.rs @@ -67,26 +67,202 @@ //! perform additional expensive checks to see if the types match or not //! (since the whole point of canonicalization is to avoid that!). -use super::{Module, RecGroupId, TypeAlloc}; +use super::{RecGroupId, TypeAlloc, TypeList}; use crate::{ types::{CoreTypeId, TypeIdentifier}, - PackedIndex, RecGroup, Result, UnpackedIndex, WasmFeatures, + BinaryReaderError, CompositeInnerType, CompositeType, PackedIndex, RecGroup, Result, + StorageType, UnpackedIndex, ValType, WasmFeatures, }; -/// Canonicalize the rec group and return its id and whether it is a new group -/// (we added its types to the `TypeAlloc`) or not (we deduplicated it with an -/// existing canonical rec group). -pub(crate) fn canonicalize_and_intern_rec_group( - features: &WasmFeatures, - types: &mut TypeAlloc, - module: &Module, - mut rec_group: RecGroup, - offset: usize, -) -> Result<(bool, RecGroupId)> { - TypeCanonicalizer::new(module, offset) - .with_features(features) - .canonicalize_rec_group(&mut rec_group)?; - Ok(types.intern_canonical_rec_group(rec_group)) +pub(crate) trait InternRecGroup { + fn add_type_id(&mut self, id: CoreTypeId); + fn type_id_at(&self, idx: u32, offset: usize) -> Result; + fn types_len(&self) -> u32; + + /// Canonicalize the rec group and return its id and whether it is a new group + /// (we added its types to the `TypeAlloc`) or not (we deduplicated it with an + /// existing canonical rec group). + fn canonicalize_and_intern_rec_group( + &mut self, + features: &WasmFeatures, + types: &mut TypeAlloc, + mut rec_group: RecGroup, + offset: usize, + ) -> Result<()> + where + Self: Sized, + { + debug_assert!(rec_group.is_explicit_rec_group() || rec_group.types().len() == 1); + if rec_group.is_explicit_rec_group() && !features.gc() { + bail!( + offset, + "rec group usage requires `gc` proposal to be enabled" + ); + } + TypeCanonicalizer::new(self, offset) + .with_features(features) + .canonicalize_rec_group(&mut rec_group)?; + let (is_new, rec_group_id) = types.intern_canonical_rec_group(rec_group); + let range = &types[rec_group_id]; + let start = range.start.index(); + let end = range.end.index(); + + for i in start..end { + let i = u32::try_from(i).unwrap(); + let id = CoreTypeId::from_index(i); + debug_assert!(types.get(id).is_some()); + self.add_type_id(id); + if is_new { + self.check_subtype(rec_group_id, id, features, types, offset)?; + } + } + + Ok(()) + } + + fn check_subtype( + &mut self, + rec_group: RecGroupId, + id: CoreTypeId, + features: &WasmFeatures, + types: &mut TypeAlloc, + offset: usize, + ) -> Result<()> { + let ty = &types[id]; + if !features.gc() && (!ty.is_final || ty.supertype_idx.is_some()) { + bail!(offset, "gc proposal must be enabled to use subtypes"); + } + + self.check_composite_type(&ty.composite_type, features, &types, offset)?; + + let depth = if let Some(supertype_index) = ty.supertype_idx { + debug_assert!(supertype_index.is_canonical()); + let sup_id = self.at_packed_index(types, rec_group, supertype_index, offset)?; + if types[sup_id].is_final { + bail!(offset, "sub type cannot have a final super type"); + } + if !types.matches(id, sup_id) { + bail!(offset, "sub type must match super type"); + } + let depth = types.get_subtyping_depth(sup_id) + 1; + if usize::from(depth) > crate::limits::MAX_WASM_SUBTYPING_DEPTH { + bail!( + offset, + "sub type hierarchy too deep: found depth {}, cannot exceed depth {}", + depth, + crate::limits::MAX_WASM_SUBTYPING_DEPTH, + ); + } + depth + } else { + 0 + }; + types.set_subtyping_depth(id, depth); + + Ok(()) + } + + fn check_composite_type( + &mut self, + ty: &CompositeType, + features: &WasmFeatures, + types: &TypeList, + offset: usize, + ) -> Result<()> { + let check = |ty: &ValType, shared: bool| { + features + .check_value_type(*ty) + .map_err(|e| BinaryReaderError::new(e, offset))?; + if shared && !types.valtype_is_shared(*ty) { + return Err(BinaryReaderError::new( + "shared composite type must contain shared types", + offset, + )); + // The other cases are fine: + // - both shared or unshared: good to go + // - the func type is unshared, `ty` is shared: though + // odd, we _can_ in fact use shared values in + // unshared composite types (e.g., functions). + } + Ok(()) + }; + if !features.shared_everything_threads() && ty.shared { + return Err(BinaryReaderError::new( + "shared composite types require the shared-everything-threads proposal", + offset, + )); + } + match &ty.inner { + CompositeInnerType::Func(t) => { + for vt in t.params().iter().chain(t.results()) { + check(vt, ty.shared)?; + } + if t.results().len() > 1 && !features.multi_value() { + return Err(BinaryReaderError::new( + "func type returns multiple values but the multi-value feature is not enabled", + offset, + )); + } + } + CompositeInnerType::Array(t) => { + if !features.gc() { + bail!( + offset, + "array indexed types not supported without the gc feature", + ); + } + if !features.gc_types() { + bail!( + offset, + "cannot define array types when gc types are disabled", + ); + } + match &t.0.element_type { + StorageType::I8 | StorageType::I16 => { + // Note: scalar types are always `shared`. + } + StorageType::Val(value_type) => check(value_type, ty.shared)?, + }; + } + CompositeInnerType::Struct(t) => { + if !features.gc() { + bail!( + offset, + "struct indexed types not supported without the gc feature", + ); + } + if !features.gc_types() { + bail!( + offset, + "cannot define struct types when gc types are disabled", + ); + } + for ft in t.fields.iter() { + match &ft.element_type { + StorageType::I8 | StorageType::I16 => { + // Note: scalar types are always `shared`. + } + StorageType::Val(value_type) => check(value_type, ty.shared)?, + } + } + } + } + Ok(()) + } + + fn at_packed_index( + &self, + types: &TypeList, + rec_group: RecGroupId, + index: PackedIndex, + offset: usize, + ) -> Result { + match index.unpack() { + UnpackedIndex::Id(id) => Ok(id), + UnpackedIndex::Module(idx) => self.type_id_at(idx, offset), + UnpackedIndex::RecGroup(idx) => types.rec_group_local_id(rec_group, idx, offset), + } + } } /// The kind of canonicalization we are doing. @@ -105,7 +281,7 @@ enum CanonicalizationMode { } pub(crate) struct TypeCanonicalizer<'a> { - module: &'a Module, + module: &'a dyn InternRecGroup, features: Option<&'a WasmFeatures>, rec_group_start: u32, rec_group_len: u32, @@ -115,7 +291,7 @@ pub(crate) struct TypeCanonicalizer<'a> { } impl<'a> TypeCanonicalizer<'a> { - pub fn new(module: &'a Module, offset: usize) -> Self { + pub fn new(module: &'a dyn InternRecGroup, offset: usize) -> Self { // These defaults will work for when we are canonicalizing types from // outside of a rec group definition, forcing all `PackedIndex`es to be // canonicalized to `CoreTypeId`s. @@ -147,7 +323,7 @@ impl<'a> TypeCanonicalizer<'a> { // Re-initialize these fields so that we properly canonicalize // intra-rec-group type references into indices into the rec group // rather than as `CoreTypeId`s. - self.rec_group_start = u32::try_from(self.module.types.len()).unwrap(); + self.rec_group_start = self.module.types_len(); self.rec_group_len = u32::try_from(rec_group.types().len()).unwrap(); for (rec_group_local_index, ty) in rec_group.types_mut().enumerate() { @@ -168,7 +344,7 @@ impl<'a> TypeCanonicalizer<'a> { fn canonicalize_type_index(&self, ty: &mut PackedIndex) -> Result<()> { match ty.unpack() { - UnpackedIndex::Id(_) => return Ok(()), + UnpackedIndex::Id(_) => Ok(()), UnpackedIndex::Module(index) => { if index < self.rec_group_start || self.mode == CanonicalizationMode::OnlyIds { let id = self.module.type_id_at(index, self.offset)?; diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index 19806666e1..db1b8b5343 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -606,23 +606,23 @@ impl From> for wasm_encoder::HeapType { fn from(r: core::HeapType<'_>) -> Self { use wasm_encoder::AbstractHeapType::*; match r { - core::HeapType::Abstract { shared, ty } => match ty { - core::AbstractHeapType::Func => Self::Abstract { shared, ty: Func }, - core::AbstractHeapType::Extern => Self::Abstract { shared, ty: Extern }, - core::AbstractHeapType::Exn | core::AbstractHeapType::NoExn => { - todo!("encoding of exceptions proposal types not yet implemented") - } - core::AbstractHeapType::Any - | core::AbstractHeapType::Eq - | core::AbstractHeapType::Struct - | core::AbstractHeapType::Array - | core::AbstractHeapType::NoFunc - | core::AbstractHeapType::NoExtern - | core::AbstractHeapType::None - | core::AbstractHeapType::I31 => { - todo!("encoding of GC proposal types not yet implemented") - } - }, + core::HeapType::Abstract { shared, ty } => { + let ty = match ty { + core::AbstractHeapType::Func => Func, + core::AbstractHeapType::Extern => Extern, + core::AbstractHeapType::Exn => Exn, + core::AbstractHeapType::NoExn => NoExn, + core::AbstractHeapType::Any => Any, + core::AbstractHeapType::Eq => Eq, + core::AbstractHeapType::Struct => Struct, + core::AbstractHeapType::Array => Array, + core::AbstractHeapType::NoFunc => NoFunc, + core::AbstractHeapType::NoExtern => NoExtern, + core::AbstractHeapType::None => None, + core::AbstractHeapType::I31 => I31, + }; + Self::Abstract { shared, ty } + } core::HeapType::Concrete(Index::Num(i, _)) => Self::Concrete(i), core::HeapType::Concrete(_) => panic!("unresolved index"), } diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index ac072c5bf1..1dfd260ad8 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -1,5 +1,5 @@ use crate::component::*; -use crate::core::{self, ValType}; +use crate::core::{self, resolve::ResolveCoreType, ValType}; use crate::kw; use crate::names::Namespace; use crate::token::Span; @@ -475,7 +475,7 @@ impl<'a> Resolver<'a> { fn core_ty(&mut self, field: &mut CoreType<'a>) -> Result<(), Error> { match &mut field.def { - CoreTypeDef::Def(_) => {} + CoreTypeDef::Def(ty) => self.stack.last_mut().unwrap().resolve_type_def(ty)?, CoreTypeDef::Module(t) => { self.stack.push(ComponentState::new(field.id)); self.module_type(t)?; @@ -766,7 +766,7 @@ impl<'a> Resolver<'a> { } impl<'a> ComponentState<'a> { - fn resolve(&mut self, ns: Ns, idx: &mut Index<'a>) -> Result { + fn resolve(&self, ns: Ns, idx: &mut Index<'a>) -> Result { match ns { Ns::CoreFunc => self.core_funcs.resolve(idx, "core func"), Ns::CoreGlobal => self.core_globals.resolve(idx, "core global"), @@ -868,6 +868,12 @@ impl<'a> ComponentState<'a> { } } +impl<'a> ResolveCoreType<'a> for ComponentState<'a> { + fn resolve_type_name(&self, name: &mut Index<'a>) -> Result { + self.resolve(Ns::CoreType, name) + } +} + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] enum Ns { CoreFunc, diff --git a/crates/wast/src/core/resolve/mod.rs b/crates/wast/src/core/resolve/mod.rs index 7b3ba2b1e3..a5bcf623db 100644 --- a/crates/wast/src/core/resolve/mod.rs +++ b/crates/wast/src/core/resolve/mod.rs @@ -6,6 +6,8 @@ mod deinline_import_export; mod names; pub(crate) mod types; +pub(crate) use names::ResolveCoreType; + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] pub enum Ns { Func, diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs index d84cd9fb35..e1ec617a31 100644 --- a/crates/wast/src/core/resolve/names.rs +++ b/crates/wast/src/core/resolve/names.rs @@ -119,22 +119,6 @@ impl<'a> Resolver<'a> { Ok(()) } - fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> { - match &mut ty.def.kind { - InnerTypeKind::Func(func) => func.resolve(self)?, - InnerTypeKind::Struct(struct_) => { - for field in &mut struct_.fields { - self.resolve_storagetype(&mut field.ty)?; - } - } - InnerTypeKind::Array(array) => self.resolve_storagetype(&mut array.ty)?, - } - if let Some(parent) = &mut ty.parent { - self.resolve(parent, Ns::Type)?; - } - Ok(()) - } - fn resolve_field(&self, field: &mut ModuleField<'a>) -> Result<(), Error> { match field { ModuleField::Import(i) => { @@ -279,36 +263,6 @@ impl<'a> Resolver<'a> { } } - fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> { - match ty { - ValType::Ref(ty) => self.resolve_heaptype(&mut ty.heap)?, - _ => {} - } - Ok(()) - } - - fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { - self.resolve_heaptype(&mut ty.heap) - } - - fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> { - match ty { - HeapType::Concrete(i) => { - self.resolve(i, Ns::Type)?; - } - _ => {} - } - Ok(()) - } - - fn resolve_storagetype(&self, ty: &mut StorageType<'a>) -> Result<(), Error> { - match ty { - StorageType::Val(ty) => self.resolve_valtype(ty)?, - _ => {} - } - Ok(()) - } - fn resolve_item_sig(&self, item: &mut ItemSig<'a>) -> Result<(), Error> { match &mut item.kind { ItemKind::Func(t) | ItemKind::Tag(TagType::Exception(t)) => { @@ -779,13 +733,76 @@ impl<'a> TypeReference<'a> for FunctionType<'a> { } fn resolve(&mut self, cx: &Resolver<'a>) -> Result<(), Error> { + cx.resolve_type_func(self) + } +} + +pub(crate) trait ResolveCoreType<'a> { + fn resolve_type_name(&self, name: &mut Index<'a>) -> Result; + + fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> { + self.resolve_type_def(&mut ty.def)?; + if let Some(parent) = &mut ty.parent { + self.resolve_type_name(parent)?; + } + Ok(()) + } + + fn resolve_type_def(&self, ty: &mut TypeDef<'a>) -> Result<(), Error> { + match &mut ty.kind { + InnerTypeKind::Func(func) => self.resolve_type_func(func), + InnerTypeKind::Struct(struct_) => { + for field in &mut struct_.fields { + self.resolve_storagetype(&mut field.ty)?; + } + Ok(()) + } + InnerTypeKind::Array(array) => self.resolve_storagetype(&mut array.ty), + } + } + + fn resolve_type_func(&self, ty: &mut FunctionType<'a>) -> Result<(), Error> { // Resolve the (ref T) value types in the final function type - for param in self.params.iter_mut() { - cx.resolve_valtype(&mut param.2)?; + for param in ty.params.iter_mut() { + self.resolve_valtype(&mut param.2)?; } - for result in self.results.iter_mut() { - cx.resolve_valtype(result)?; + for result in ty.results.iter_mut() { + self.resolve_valtype(result)?; } Ok(()) } + + fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> { + match ty { + ValType::Ref(ty) => self.resolve_reftype(ty), + ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => Ok(()), + } + } + + fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { + self.resolve_heaptype(&mut ty.heap) + } + + fn resolve_heaptype(&self, ty: &mut HeapType<'a>) -> Result<(), Error> { + match ty { + HeapType::Concrete(i) => { + self.resolve_type_name(i)?; + } + HeapType::Abstract { .. } => {} + } + Ok(()) + } + + fn resolve_storagetype(&self, ty: &mut StorageType<'a>) -> Result<(), Error> { + match ty { + StorageType::Val(ty) => self.resolve_valtype(ty), + StorageType::I8 | StorageType::I16 => Ok(()), + } + } +} + +impl<'a> ResolveCoreType<'a> for Resolver<'a> { + fn resolve_type_name(&self, name: &mut Index<'a>) -> Result { + self.resolve(name, Ns::Type) + } } diff --git a/tests/local/component-model/types.wast b/tests/local/component-model/types.wast index 34115c942f..0323cab8e7 100644 --- a/tests/local/component-model/types.wast +++ b/tests/local/component-model/types.wast @@ -357,3 +357,45 @@ )) ) "cannot have more than 32 flags") + +;; test components with non-mvp types +(component + ;; all abstract heap types work + (core type (func (param (ref any)))) + (core type (func (param (ref func)))) + (core type (func (param (ref extern)))) + (core type (func (param (ref exn)))) + (core type (func (param (ref noexn)))) + (core type (func (param (ref eq)))) + (core type (func (param (ref struct)))) + (core type (func (param (ref array)))) + (core type (func (param (ref nofunc)))) + (core type (func (param (ref noextern)))) + (core type (func (param (ref none)))) + (core type (func (param (ref i31)))) + + ;; some shorthands work + (core type (func (param anyref))) + (core type (func (param eqref))) + + ;; simd types work + (core type (func (param v128))) + + ;; types-pointing-to-types works + (core type $t (func)) + (core type (func (param (ref $t)))) + +) + +(assert_invalid + (component + (core type $t (module)) + (core type (func (param (ref $t)))) + ) + "type index 0 is a module type") + +(assert_invalid + (component + (core type (func (param (ref 100)))) + ) + "type index out of bounds") diff --git a/tests/local/missing-features/component-model/types.wast b/tests/local/missing-features/component-model/types.wast new file mode 100644 index 0000000000..b4d4772d3d --- /dev/null +++ b/tests/local/missing-features/component-model/types.wast @@ -0,0 +1,18 @@ +(assert_invalid + (component + (core type (func (param v128))) + ) + "SIMD support is not enabled") + +(assert_invalid + (component + (core type (func (param (ref 0)))) + ) + "unknown type 0: type index out of bounds") + +(assert_invalid + (component + (core type (func)) + (core type (func (param (ref 0)))) + ) + "reference types support is not enabled") diff --git a/tests/snapshots/local/component-model/types.wast.json b/tests/snapshots/local/component-model/types.wast.json index 8279f094ca..a606256226 100644 --- a/tests/snapshots/local/component-model/types.wast.json +++ b/tests/snapshots/local/component-model/types.wast.json @@ -283,6 +283,25 @@ "filename": "types.41.wasm", "text": "cannot have more than 32 flags", "module_type": "binary" + }, + { + "type": "module", + "line": 362, + "filename": "types.42.wasm" + }, + { + "type": "assert_invalid", + "line": 391, + "filename": "types.43.wasm", + "text": "type index 0 is a module type", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 398, + "filename": "types.44.wasm", + "text": "type index out of bounds", + "module_type": "binary" } ] } \ No newline at end of file diff --git a/tests/snapshots/local/component-model/types.wast/42.print b/tests/snapshots/local/component-model/types.wast/42.print new file mode 100644 index 0000000000..233982b2c2 --- /dev/null +++ b/tests/snapshots/local/component-model/types.wast/42.print @@ -0,0 +1,19 @@ +(component + (core type (;0;) (func (param (ref any)))) + (core type (;1;) (func (param (ref func)))) + (core type (;2;) (func (param (ref extern)))) + (core type (;3;) (func (param (ref exn)))) + (core type (;4;) (func (param (ref noexn)))) + (core type (;5;) (func (param (ref eq)))) + (core type (;6;) (func (param (ref struct)))) + (core type (;7;) (func (param (ref array)))) + (core type (;8;) (func (param (ref nofunc)))) + (core type (;9;) (func (param (ref noextern)))) + (core type (;10;) (func (param (ref none)))) + (core type (;11;) (func (param (ref i31)))) + (core type (;12;) (func (param anyref))) + (core type (;13;) (func (param eqref))) + (core type (;14;) (func (param v128))) + (core type $t (;15;) (func)) + (core type (;16;) (func (param (ref $t)))) +) diff --git a/tests/snapshots/local/missing-features/component-model/types.wast.json b/tests/snapshots/local/missing-features/component-model/types.wast.json new file mode 100644 index 0000000000..876d023287 --- /dev/null +++ b/tests/snapshots/local/missing-features/component-model/types.wast.json @@ -0,0 +1,26 @@ +{ + "source_filename": "tests/local/missing-features/component-model/types.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "types.0.wasm", + "text": "SIMD support is not enabled", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 8, + "filename": "types.1.wasm", + "text": "unknown type 0: type index out of bounds", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 14, + "filename": "types.2.wasm", + "text": "reference types support is not enabled", + "module_type": "binary" + } + ] +} \ No newline at end of file