diff --git a/crates/wasm-mutate/src/info.rs b/crates/wasm-mutate/src/info.rs index 54b0f64676..3d2415ee73 100644 --- a/crates/wasm-mutate/src/info.rs +++ b/crates/wasm-mutate/src/info.rs @@ -92,13 +92,19 @@ impl<'a> ModuleInfo<'a> { info.section(SectionId::Type.into(), reader.range(), input_wasm); // Save function types - for ty in reader { - if let Ok(st) = ty { - if st.is_final || st.supertype_idx.is_some() { + for rec_group in reader { + if let Ok(rg) = rec_group { + if rg.types().len() != 1 { return Err(Error::unsupported("GC types not supported yet")); } - let typeinfo = TypeInfo::try_from(st.structural_type).unwrap(); - info.types_map.push(typeinfo); + for st in rg.types() { + if st.is_final || st.supertype_idx.is_some() { + return Err(Error::unsupported("GC types not supported yet")); + } + let typeinfo = + TypeInfo::try_from(st.clone().structural_type).unwrap(); + info.types_map.push(typeinfo); + } } } } diff --git a/crates/wasm-mutate/src/mutators/add_type.rs b/crates/wasm-mutate/src/mutators/add_type.rs index 6de1719ee3..ce28941238 100644 --- a/crates/wasm-mutate/src/mutators/add_type.rs +++ b/crates/wasm-mutate/src/mutators/add_type.rs @@ -55,28 +55,30 @@ impl Mutator for AddTypeMutator { if let Some(old_types) = config.info().get_type_section() { // Copy the existing types section over into the encoder. let reader = wasmparser::TypeSectionReader::new(old_types.data, 0)?; - for ty in reader { - match ty?.structural_type { - wasmparser::StructuralType::Func(ty) => { - let params = ty - .params() - .iter() - .copied() - .map(map_type) - .collect::, _>>()?; - let results = ty - .results() - .iter() - .copied() - .map(map_type) - .collect::, _>>()?; - types.function(params, results); - } - wasmparser::StructuralType::Array(_) => { - return Err(Error::unsupported("Array types are not supported yet.")); - } - wasmparser::StructuralType::Struct(_) => { - return Err(Error::unsupported("Struct types are not supported yet.")); + for rec_group in reader { + for ty in rec_group?.types() { + match ty.clone().structural_type { + wasmparser::StructuralType::Func(ty) => { + let params = ty + .params() + .iter() + .copied() + .map(map_type) + .collect::, _>>()?; + let results = ty + .results() + .iter() + .copied() + .map(map_type) + .collect::, _>>()?; + types.function(params, results); + } + wasmparser::StructuralType::Array(_) => { + return Err(Error::unsupported("Array types are not supported yet.")); + } + wasmparser::StructuralType::Struct(_) => { + return Err(Error::unsupported("Struct types are not supported yet.")); + } } } } diff --git a/crates/wasm-mutate/src/mutators/remove_item.rs b/crates/wasm-mutate/src/mutators/remove_item.rs index 96e8515fe3..b6e8485112 100644 --- a/crates/wasm-mutate/src/mutators/remove_item.rs +++ b/crates/wasm-mutate/src/mutators/remove_item.rs @@ -136,7 +136,12 @@ impl RemoveItem { 0, TypeSectionReader::new(section.data, 0)?, Item::Type, - |me, ty, section| me.translate_type_def(ty.structural_type, section), + |me, rec_group, section| { + for ty in rec_group.types() { + me.translate_type_def(ty.clone().structural_type, section)?; + } + Ok(()) + }, )?; }, diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index e4468a8ebd..6943f8b84d 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -553,8 +553,10 @@ impl Module { match payload.expect("could not parse the available import payload") { wasmparser::Payload::TypeSection(type_reader) => { for ty in type_reader { - let ty = ty.expect("could not parse type section"); - available_types.push((ty.structural_type, None)); + let rec_group = ty.expect("could not parse type section"); + for ty in rec_group.types() { + available_types.push((ty.clone().structural_type, None)); + } } } wasmparser::Payload::ImportSection(import_reader) => { diff --git a/crates/wasm-smith/tests/core.rs b/crates/wasm-smith/tests/core.rs index c63b9644c4..ada3efab45 100644 --- a/crates/wasm-smith/tests/core.rs +++ b/crates/wasm-smith/tests/core.rs @@ -129,14 +129,16 @@ fn smoke_test_imports_config() { let payload = payload.unwrap(); if let wasmparser::Payload::TypeSection(rdr) = payload { // Gather the signature types to later check function types against. - for ty in rdr { - match ty.unwrap().structural_type { - wasmparser::StructuralType::Func(ft) => sig_types.push(ft), - wasmparser::StructuralType::Array(_) => { - unimplemented!("Array types are not supported yet.") - } - wasmparser::StructuralType::Struct(_) => { - unimplemented!("Struct types are not supported yet.") + for rec_group in rdr { + for ty in rec_group.unwrap().types() { + match ty.clone().structural_type { + wasmparser::StructuralType::Func(ft) => sig_types.push(ft), + wasmparser::StructuralType::Array(_) => { + unimplemented!("Array types are not supported yet.") + } + wasmparser::StructuralType::Struct(_) => { + unimplemented!("Struct types are not supported yet.") + } } } } diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs index 6d89c3ecb8..17075a0776 100644 --- a/crates/wasmparser/src/readers/core/types.rs +++ b/crates/wasmparser/src/readers/core/types.rs @@ -13,12 +13,14 @@ * limitations under the License. */ +use std::fmt::{self, Debug, Write}; +use std::slice; + use crate::limits::{ MAX_WASM_FUNCTION_PARAMS, MAX_WASM_FUNCTION_RETURNS, MAX_WASM_STRUCT_FIELDS, - MAX_WASM_SUPERTYPES, + MAX_WASM_SUPERTYPES, MAX_WASM_TYPES, }; use crate::{BinaryReader, BinaryReaderError, FromReader, Result, SectionLimited}; -use std::fmt::{self, Debug, Write}; /// Represents the types of values in a WebAssembly module. #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] @@ -54,7 +56,13 @@ const _: () = { }; pub(crate) trait Inherits { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType; } @@ -99,12 +107,20 @@ impl ValType { } impl Inherits for ValType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { match (self, other) { - (Self::Ref(r1), Self::Ref(r2)) => r1.inherits(r2, type_at), + (Self::Ref(r1), Self::Ref(r2)) => { + r1.inherits(r2, self_base_idx, other_base_idx, type_at) + } ( s @ (Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 | Self::Ref(_)), o, @@ -535,13 +551,24 @@ impl RefType { } impl Inherits for RefType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { self == other || ((other.is_nullable() || !self.is_nullable()) - && self.heap_type().inherits(&other.heap_type(), type_at)) + && self.heap_type().inherits( + &other.heap_type(), + self_base_idx, + other_base_idx, + type_at, + )) } } @@ -630,14 +657,33 @@ pub enum HeapType { I31, } +fn choose_base(base_idx: Option, idx: u32) -> Option { + if base_idx == None || idx == base_idx.unwrap() || idx < base_idx.unwrap() { + Some(idx) + } else { + base_idx + } +} + impl Inherits for HeapType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { match (self, other) { (HeapType::Indexed(a), HeapType::Indexed(b)) => { - a == b || type_at(*a).inherits(type_at(*b), type_at) + a == b || { + let a_base = choose_base(self_base_idx, *a); + let b_base = choose_base(other_base_idx, *b); + (a_base == self_base_idx && b_base == other_base_idx) + || type_at(*a).inherits(type_at(*b), a_base, b_base, type_at) + } } (HeapType::Indexed(a), HeapType::Func) => match type_at(*a).structural_type { StructuralType::Func(_) => true, @@ -771,15 +817,48 @@ pub struct SubType { pub structural_type: StructuralType, } +/// Represents a recursive type group in a WebAssembly module. +#[derive(Debug, Clone)] +pub struct RecGroup { + items: RecGroupItems, +} + +#[derive(Debug, Clone)] +enum RecGroupItems { + /// The list of subtypes in the recursive type group. + Many(Vec), + /// A single subtype in the recursive type group. + Single(SubType), +} + +impl RecGroup { + /// Returns the list of subtypes in the recursive type group. + pub fn types(&self) -> &[SubType] { + match &self.items { + RecGroupItems::Many(types) => types, + RecGroupItems::Single(ty) => slice::from_ref(ty), + } + } +} + impl Inherits for SubType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { !other.is_final - && self - .structural_type - .inherits(&other.structural_type, type_at) + && self.structural_type.inherits( + &other.structural_type, + self_base_idx, + other_base_idx, + type_at, + ) } } @@ -813,14 +892,26 @@ pub struct StructType { } impl Inherits for StructuralType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { match (self, other) { - (StructuralType::Func(a), StructuralType::Func(b)) => a.inherits(b, type_at), - (StructuralType::Array(a), StructuralType::Array(b)) => a.inherits(b, type_at), - (StructuralType::Struct(a), StructuralType::Struct(b)) => a.inherits(b, type_at), + (StructuralType::Func(a), StructuralType::Func(b)) => { + a.inherits(b, self_base_idx, other_base_idx, type_at) + } + (StructuralType::Array(a), StructuralType::Array(b)) => { + a.inherits(b, self_base_idx, other_base_idx, type_at) + } + (StructuralType::Struct(a), StructuralType::Struct(b)) => { + a.inherits(b, self_base_idx, other_base_idx, type_at) + } (StructuralType::Func(_), _) => false, (StructuralType::Array(_), _) => false, (StructuralType::Struct(_), _) => false, @@ -900,7 +991,13 @@ impl FuncType { } impl Inherits for FuncType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { @@ -912,47 +1009,78 @@ impl Inherits for FuncType { .params() .iter() .zip(other.params()) - .fold(true, |r, (a, b)| r && b.inherits(a, type_at)) + .fold(true, |r, (a, b)| r && b.inherits(a, self_base_idx, other_base_idx, type_at)) && self .results() .iter() .zip(other.results()) - .fold(true, |r, (a, b)| r && a.inherits(b, type_at)) + .fold(true, |r, (a, b)| r && a.inherits(b, self_base_idx, other_base_idx, type_at)) } } impl Inherits for ArrayType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { - self.0.inherits(&other.0, type_at) + self.0 + .inherits(&other.0, self_base_idx, other_base_idx, type_at) } } impl Inherits for FieldType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { - (other.mutable || !self.mutable) && self.element_type.inherits(&other.element_type, type_at) + (other.mutable || !self.mutable) + && self.element_type.inherits( + &other.element_type, + self_base_idx, + other_base_idx, + type_at, + ) } } impl Inherits for StorageType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { match (self, other) { - (Self::Val(a), Self::Val(b)) => a.inherits(b, type_at), + (Self::Val(a), Self::Val(b)) => a.inherits(b, self_base_idx, other_base_idx, type_at), (a @ (Self::I8 | Self::I16 | Self::Val(_)), b) => a == b, } } } impl Inherits for StructType { - fn inherits<'a, F>(&self, other: &Self, type_at: &F) -> bool + fn inherits<'a, F>( + &self, + other: &Self, + self_base_idx: Option, + other_base_idx: Option, + type_at: &F, + ) -> bool where F: Fn(u32) -> &'a SubType, { @@ -962,7 +1090,9 @@ impl Inherits for StructType { .fields .iter() .zip(other.fields.iter()) - .fold(true, |r, (a, b)| r && a.inherits(b, type_at)) + .fold(true, |r, (a, b)| { + r && a.inherits(b, self_base_idx, other_base_idx, type_at) + }) } } @@ -1044,7 +1174,7 @@ pub struct TagType { } /// A reader for the type section of a WebAssembly module. -pub type TypeSectionReader<'a> = SectionLimited<'a, SubType>; +pub type TypeSectionReader<'a> = SectionLimited<'a, RecGroup>; impl<'a> FromReader<'a> for StructuralType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { @@ -1064,6 +1194,23 @@ fn read_structural_type( }) } +impl<'a> FromReader<'a> for RecGroup { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result { + Ok(match reader.peek()? { + 0x4f => { + reader.read_u8()?; + let types = reader.read_iter(MAX_WASM_TYPES, "rec group types")?; + RecGroup { + items: RecGroupItems::Many(types.collect::>()?), + } + } + _ => RecGroup { + items: RecGroupItems::Single(reader.read()?), + }, + }) + } +} + impl<'a> FromReader<'a> for SubType { fn from_reader(reader: &mut BinaryReader<'a>) -> Result { let pos = reader.original_position(); diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 48f404ff2a..2b747f83a5 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -611,7 +611,8 @@ impl Validator { state .module .assert_mut() - .add_type(def, features, types, offset, false /* checked above */) + .add_types(def.types(), features, types, offset, true)?; + Ok(()) }, ) } diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 9c5eaa15c1..b0d98e82fa 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1427,7 +1427,7 @@ impl ComponentState { for decl in decls { match decl { crate::ModuleTypeDeclaration::Type(ty) => { - state.add_type(ty, features, types, offset, true)?; + state.add_types(&[ty], features, types, offset, true)?; } crate::ModuleTypeDeclaration::Export { name, ty } => { let ty = state.check_type_ref(&ty, features, types, offset)?; diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 9ee4504427..c123b8b031 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -1,10 +1,10 @@ //! State relating to validating a WebAssembly module. //! -use super::{ - check_max, combine_type_sizes, - operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations}, - types::{EntityType, Type, TypeAlloc, TypeId, TypeList}, -}; +use std::mem; +use std::{collections::HashSet, sync::Arc}; + +use indexmap::IndexMap; + use crate::limits::*; use crate::readers::Inherits; use crate::validator::core::arc::MaybeOwned; @@ -14,9 +14,12 @@ use crate::{ SubType, Table, TableInit, TableType, TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmModuleResources, }; -use indexmap::IndexMap; -use std::mem; -use std::{collections::HashSet, sync::Arc}; + +use super::{ + check_max, combine_type_sizes, + operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations}, + types::{EntityType, Type, TypeAlloc, TypeId, TypeList}, +}; // Section order for WebAssembly modules. // @@ -493,56 +496,77 @@ pub(crate) struct Module { } impl Module { - pub fn add_type( + pub fn add_types( &mut self, - ty: SubType, + types_added: &[SubType], features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, check_limit: bool, ) -> Result<()> { if check_limit { - check_max(self.types.len(), 1, MAX_WASM_TYPES, "types", offset)?; + check_max( + self.types.len(), + types_added.len() as u32, + MAX_WASM_TYPES, + "types", + offset, + )?; } - let ty = self.check_subtype(ty, features, types, offset)?; + let idx_types: Vec<_> = types_added + .iter() + .map(|ty| { + let id = types.push_ty(Type::Sub(ty.clone())); + if features.gc { + // make types in a rec group resolvable by index before validation: + // this is needed to support recursive types in the GC proposal + self.types.push(id); + } + (id, ty) + }) + .collect(); - let id = types.push_ty(ty); - self.types.push(id); + for (id, ty) in idx_types { + self.check_subtype(id.index as u32, ty.clone(), features, types, offset)?; + if !features.gc { + self.types.push(id); + } + } Ok(()) } fn check_subtype( &mut self, + type_index: u32, ty: SubType, features: &WasmFeatures, types: &mut TypeAlloc, offset: usize, ) -> Result { if !features.gc && (ty.is_final || ty.supertype_idx.is_some()) { - return Err(BinaryReaderError::new( - "gc proposal must be enabled to use subtypes", - offset, - )); + bail!(offset, "gc proposal must be enabled to use subtypes"); } self.check_structural_type(&ty.structural_type, features, offset)?; - if let Some(type_index) = ty.supertype_idx { + if let Some(supertype_index) = ty.supertype_idx { // Check the supertype exists, is not final, and the subtype matches it. - match self.type_at(types, type_index, offset)? { + if supertype_index >= type_index { + bail!( + offset, + "unknown type {type_index}: type index out of bounds" + ); + } + match self.type_at(types, supertype_index, offset)? { Type::Sub(st) => { - if !&ty.inherits(st, &|idx| self.subtype_at(types, idx, offset).unwrap()) { - return Err(BinaryReaderError::new( - "subtype must match supertype", - offset, - )); + if !&ty.inherits(st, Some(type_index), Some(supertype_index), &|idx| { + self.subtype_at(types, idx, offset).unwrap() + }) { + bail!(offset, "subtype must match supertype"); } } _ => { - return Err(BinaryReaderError::new( - "supertype must be a non-final subtype itself", - offset, - )); + bail!(offset, "supertype must be a non-final subtype itself"); } }; } @@ -926,7 +950,9 @@ impl Module { /// E.g. a non-nullable reference can be assigned to a nullable reference, but not vice versa. /// Or an indexed func ref is assignable to a generic func ref, but not vice versa. pub(crate) fn matches(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool { - ty1.inherits(&ty2, &|idx| self.subtype_at(types, idx, 0).unwrap()) + ty1.inherits(&ty2, None, None, &|idx| { + self.subtype_at(types, idx, 0).unwrap() + }) } fn check_tag_type( diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index e68dc703ee..4a87795c3e 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -662,6 +662,16 @@ impl Printer { Ok(()) } + fn print_rec(&mut self, state: &mut State, offset: usize, rg: RecGroup) -> Result<()> { + self.start_group("rec"); + for ty in rg.types() { + self.newline(offset + 2); + self.print_type(state, ty.clone())?; + } + self.end_group(); // `rec` + Ok(()) + } + fn print_type(&mut self, state: &mut State, ty: SubType) -> Result<()> { self.start_group("type "); self.print_name(&state.core.type_names, state.core.types.len() as u32)?; @@ -730,9 +740,13 @@ impl Printer { fn print_types(&mut self, state: &mut State, parser: TypeSectionReader<'_>) -> Result<()> { for ty in parser.into_iter_with_offsets() { - let (offset, ty) = ty?; + let (offset, rec_group) = ty?; self.newline(offset); - self.print_type(state, ty)?; + if rec_group.types().len() == 1 { + self.print_type(state, rec_group.types()[0].clone())?; + } else { + self.print_rec(state, offset, rec_group)?; + } } Ok(()) diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index 3e85e00e78..49a0147129 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -245,8 +245,10 @@ impl<'a> Module<'a> { } Payload::End(_) => {} Payload::TypeSection(s) => { - for ty in s { - self.types.push(ty?); + for rec_group in s { + for ty in rec_group?.types() { + self.types.push(ty.clone()); + } } } Payload::ImportSection(s) => { diff --git a/crates/wit-component/src/linking/metadata.rs b/crates/wit-component/src/linking/metadata.rs index d1f1d2eda7..63a487f84d 100644 --- a/crates/wit-component/src/linking/metadata.rs +++ b/crates/wit-component/src/linking/metadata.rs @@ -344,8 +344,16 @@ impl<'a> Metadata<'a> { Payload::TypeSection(reader) => { types = reader .into_iter() + .flat_map(|r| match r { + Err(e) => vec![Err(e)], + Ok(rg) => rg + .types() + .iter() + .map(|st| Ok(st.clone())) + .collect::>(), + }) .filter_map(|r| { - r.map(|ty| match ty.structural_type { + r.map(|ty| match ty.clone().structural_type { StructuralType::Func(ty) => Some(ty), StructuralType::Array(_) | StructuralType::Struct(_) => None, }) diff --git a/tests/cli/dump/alias.wat.stdout b/tests/cli/dump/alias.wat.stdout index 3f11da9812..b84d1a53b4 100644 --- a/tests/cli/dump/alias.wat.stdout +++ b/tests/cli/dump/alias.wat.stdout @@ -30,7 +30,7 @@ | 01 00 00 00 0x57 | 01 04 | type section 0x59 | 01 | 1 count - 0x5a | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0x5a | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x5d | 03 02 | func section 0x5f | 01 | 1 count 0x60 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/alias2.wat.stdout b/tests/cli/dump/alias2.wat.stdout index 661b1d7a02..08bee8f6d4 100644 --- a/tests/cli/dump/alias2.wat.stdout +++ b/tests/cli/dump/alias2.wat.stdout @@ -126,7 +126,7 @@ | 01 00 00 00 0x158 | 01 04 | type section 0x15a | 01 | 1 count - 0x15b | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0x15b | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x15e | 03 02 | func section 0x160 | 01 | 1 count 0x161 | 00 | [func 0] type 0 @@ -162,7 +162,7 @@ | 01 00 00 00 0x1a2 | 01 04 | type section 0x1a4 | 01 | 1 count - 0x1a5 | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0x1a5 | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x1a8 | 02 19 | import section 0x1aa | 04 | 4 count 0x1ab | 00 01 31 00 | import [func 0] Import { module: "", name: "1", ty: Func(0) } diff --git a/tests/cli/dump/blockty.wat.stdout b/tests/cli/dump/blockty.wat.stdout index b17b39084f..48152cdef9 100644 --- a/tests/cli/dump/blockty.wat.stdout +++ b/tests/cli/dump/blockty.wat.stdout @@ -2,12 +2,12 @@ | 01 00 00 00 0x8 | 01 17 | type section 0xa | 05 | 5 count - 0xb | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } - 0xe | 60 00 01 7f | [type 1] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [I32] }) } - 0x12 | 60 01 7f 00 | [type 2] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) } - 0x16 | 60 01 7f 01 | [type 3] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32] }) } + 0xb | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } + 0xe | 60 00 01 7f | [type 1] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [I32] }) }) } + 0x12 | 60 01 7f 00 | [type 2] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) }) } + 0x16 | 60 01 7f 01 | [type 3] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32] }) }) } | 7f - 0x1b | 60 01 7f 02 | [type 4] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32, I32] }) } + 0x1b | 60 01 7f 02 | [type 4] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32, I32] }) }) } | 7f 7f 0x21 | 03 02 | func section 0x23 | 01 | 1 count diff --git a/tests/cli/dump/bundled.wat.stdout b/tests/cli/dump/bundled.wat.stdout index e5c9b09780..f2af88329e 100644 --- a/tests/cli/dump/bundled.wat.stdout +++ b/tests/cli/dump/bundled.wat.stdout @@ -25,7 +25,7 @@ | 01 00 00 00 0x54 | 01 09 | type section 0x56 | 01 | 1 count - 0x57 | 60 04 7f 7f | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32, I32], returns: [I32] }) } + 0x57 | 60 04 7f 7f | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32, I32], returns: [I32] }) }) } | 7f 7f 01 7f 0x5f | 03 02 | func section 0x61 | 01 | 1 count @@ -61,9 +61,9 @@ | 01 00 00 00 0xa0 | 01 09 | type section 0xa2 | 02 | 2 count - 0xa3 | 60 02 7f 7f | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) } + 0xa3 | 60 02 7f 7f | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) }) } | 00 - 0xa8 | 60 00 00 | [type 1] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0xa8 | 60 00 00 | [type 1] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0xab | 02 12 | import section 0xad | 01 | 1 count 0xae | 09 77 61 73 | import [func 0] Import { module: "wasi-file", name: "read", ty: Func(0) } @@ -103,9 +103,9 @@ | 01 00 00 00 0x101 | 01 0c | type section 0x103 | 02 | 2 count - 0x104 | 60 02 7f 7f | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) } + 0x104 | 60 02 7f 7f | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) }) } | 00 - 0x109 | 60 03 7f 7f | [type 1] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32], returns: [] }) } + 0x109 | 60 03 7f 7f | [type 1] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32], returns: [] }) }) } | 7f 00 0x10f | 02 12 | import section 0x111 | 01 | 1 count diff --git a/tests/cli/dump/component-expand-bundle.wat.stdout b/tests/cli/dump/component-expand-bundle.wat.stdout index 985491a8ae..ef3b012489 100644 --- a/tests/cli/dump/component-expand-bundle.wat.stdout +++ b/tests/cli/dump/component-expand-bundle.wat.stdout @@ -5,7 +5,7 @@ | 01 00 00 00 0x12 | 01 04 | type section 0x14 | 01 | 1 count - 0x15 | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0x15 | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x18 | 03 02 | func section 0x1a | 01 | 1 count 0x1b | 00 | [func 0] type 0 @@ -28,7 +28,7 @@ | 01 00 00 00 0x3d | 01 04 | type section 0x3f | 01 | 1 count - 0x40 | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0x40 | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x43 | 02 06 | import section 0x45 | 01 | 1 count 0x46 | 00 01 61 00 | import [func 0] Import { module: "", name: "a", ty: Func(0) } diff --git a/tests/cli/dump/import-modules.wat.stdout b/tests/cli/dump/import-modules.wat.stdout index 3006c2a256..8b2cae6eba 100644 --- a/tests/cli/dump/import-modules.wat.stdout +++ b/tests/cli/dump/import-modules.wat.stdout @@ -14,7 +14,7 @@ | 01 00 00 00 0x2a | 01 04 | type section 0x2c | 01 | 1 count - 0x2d | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0x2d | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x30 | 03 02 | func section 0x32 | 01 | 1 count 0x33 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/names.wat.stdout b/tests/cli/dump/names.wat.stdout index a8e33bb1c5..96f641bf37 100644 --- a/tests/cli/dump/names.wat.stdout +++ b/tests/cli/dump/names.wat.stdout @@ -2,7 +2,7 @@ | 01 00 00 00 0x8 | 01 05 | type section 0xa | 01 | 1 count - 0xb | 60 01 7f 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) } + 0xb | 60 01 7f 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) }) } 0xf | 03 02 | func section 0x11 | 01 | 1 count 0x12 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/select.wat.stdout b/tests/cli/dump/select.wat.stdout index fd63bd0001..3a6212e193 100644 --- a/tests/cli/dump/select.wat.stdout +++ b/tests/cli/dump/select.wat.stdout @@ -2,7 +2,7 @@ | 01 00 00 00 0x8 | 01 04 | type section 0xa | 01 | 1 count - 0xb | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0xb | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0xe | 03 02 | func section 0x10 | 01 | 1 count 0x11 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/simple.wat.stdout b/tests/cli/dump/simple.wat.stdout index b7e11a8799..2cfb2d3ed3 100644 --- a/tests/cli/dump/simple.wat.stdout +++ b/tests/cli/dump/simple.wat.stdout @@ -2,8 +2,8 @@ | 01 00 00 00 0x8 | 01 08 | type section 0xa | 02 | 2 count - 0xb | 60 01 7f 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) } - 0xf | 60 00 00 | [type 1] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0xb | 60 01 7f 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) }) } + 0xf | 60 00 00 | [type 1] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0x12 | 02 07 | import section 0x14 | 01 | 1 count 0x15 | 01 6d 01 6e | import [func 0] Import { module: "m", name: "n", ty: Func(0) } diff --git a/tests/cli/dump/try-delegate.wat.stdout b/tests/cli/dump/try-delegate.wat.stdout index c74f476530..dc09a05d36 100644 --- a/tests/cli/dump/try-delegate.wat.stdout +++ b/tests/cli/dump/try-delegate.wat.stdout @@ -2,7 +2,7 @@ | 01 00 00 00 0x8 | 01 04 | type section 0xa | 01 | 1 count - 0xb | 60 00 00 | [type 0] SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) } + 0xb | 60 00 00 | [type 0] RecGroup { items: Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) }) } 0xe | 03 02 | func section 0x10 | 01 | 1 count 0x11 | 00 | [func 0] type 0 diff --git a/tests/local/gc/gc-rec-groups-invalid.wast b/tests/local/gc/gc-rec-groups-invalid.wast new file mode 100644 index 0000000000..b2de1893c3 --- /dev/null +++ b/tests/local/gc/gc-rec-groups-invalid.wast @@ -0,0 +1,16 @@ +;; --enable-gc + +(assert_invalid + (module + (rec + (type $t1 (struct (field (ref $t2)))) + (type $t2 (struct (field (ref $t3)))) ;; <--- unknown type + ) + + (rec + (type $t3 (struct (field (ref $t4)))) + (type $t4 (sub $t2 (struct (field (ref $t3))))) + ) + ) + "unknown type" +) diff --git a/tests/local/gc/gc-rec-sub.wat b/tests/local/gc/gc-rec-sub.wat index cfea4f732a..c4deded8ac 100644 --- a/tests/local/gc/gc-rec-sub.wat +++ b/tests/local/gc/gc-rec-sub.wat @@ -39,4 +39,16 @@ (type (sub final $a (func))) ) (type (sub $a (func))) + + (rec + (type $t1 (struct (field (ref $t2)))) + (type $t2 (struct (field (ref $t1)))) + ) + + (rec + (type $t3 (struct (field (ref $t4)))) + (type $t4 (sub $t2 (struct (field (ref $t3))))) + ) + + (rec) ) diff --git a/tests/local/gc/type-equivalence.wast b/tests/local/gc/type-equivalence.wast new file mode 100644 index 0000000000..3f785fe7ae --- /dev/null +++ b/tests/local/gc/type-equivalence.wast @@ -0,0 +1,134 @@ +;; Cloned from tests/testsuite/proposals/function-references/type-equivalence.wast +;; and modified for the GC proposal. + +;; Syntactic types (validation time) + +;; Simple types. + +(module + (type $t1 (func (param f32 f32) (result f32))) + (type $t2 (func (param $x f32) (param $y f32) (result f32))) + + (func $f1 (param $r (ref $t1)) (call $f2 (local.get $r))) + (func $f2 (param $r (ref $t2)) (call $f1 (local.get $r))) +) + + +;; Indirect types. + +(module + (type $s0 (func (param i32) (result f32))) + (type $s1 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $t1 (func (param (ref $s1)) (result (ref $s2)))) + (type $t2 (func (param (ref $s2)) (result (ref $s1)))) + + (func $f1 (param $r (ref $t1)) (call $f2 (local.get $r))) + (func $f2 (param $r (ref $t2)) (call $f1 (local.get $r))) +) + + +;; Recursive types. + +;; This case has been made valid by the GC proposal: +;; every standalone type is now a recursive type group of size 1. +(module + (type $t (func (result (ref $t)))) +) + +(assert_invalid + (module + (type $t1 (func (param (ref $t2)))) + (type $t2 (func (param (ref $t1)))) + ) + "unknown type" +) + + +;; Semantic types (run time) + +;; Simple types. + +(module + (type $t1 (func (param f32 f32))) + (type $t2 (func (param $x f32) (param $y f32))) + + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (table funcref (elem $f1 $f2)) + + (func (export "run") + (call_indirect (type $t1) (f32.const 1) (f32.const 2) (i32.const 1)) + (call_indirect (type $t2) (f32.const 1) (f32.const 2) (i32.const 0)) + ) +) +(assert_return (invoke "run")) + + +;; Indirect types. + +(module + (type $s0 (func (param i32))) + (type $s1 (func (param i32 (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)))) + (type $t1 (func (param (ref $s1)))) + (type $t2 (func (param (ref $s2)))) + + (func $s1 (type $s1)) + (func $s2 (type $s2)) + (func $f1 (type $t1)) + (func $f2 (type $t2)) + (table funcref (elem $f1 $f2 $s1 $s2)) + + (func (export "run") + (call_indirect (type $t1) (ref.func $s1) (i32.const 0)) + (call_indirect (type $t1) (ref.func $s1) (i32.const 1)) + (call_indirect (type $t1) (ref.func $s2) (i32.const 0)) + (call_indirect (type $t1) (ref.func $s2) (i32.const 1)) + (call_indirect (type $t2) (ref.func $s1) (i32.const 0)) + (call_indirect (type $t2) (ref.func $s1) (i32.const 1)) + (call_indirect (type $t2) (ref.func $s2) (i32.const 0)) + (call_indirect (type $t2) (ref.func $s2) (i32.const 1)) + ) +) +(assert_return (invoke "run")) + + +;; Semantic types (link time) + +;; Simple types. + +(module + (type $t1 (func (param f32 f32) (result f32))) + (func (export "f") (param (ref $t1))) +) +(register "M") +(module + (type $t2 (func (param $x f32) (param $y f32) (result f32))) + (func (import "M" "f") (param (ref $t2))) +) + + +;; Indirect types. + +(module + (type $s0 (func (param i32) (result f32))) + (type $s1 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $t1 (func (param (ref $s1)) (result (ref $s2)))) + (type $t2 (func (param (ref $s2)) (result (ref $s1)))) + (func (export "f1") (param (ref $t1))) + (func (export "f2") (param (ref $t1))) +) +(register "M") +(module + (type $s0 (func (param i32) (result f32))) + (type $s1 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $s2 (func (param i32 (ref $s0)) (result (ref $s0)))) + (type $t1 (func (param (ref $s1)) (result (ref $s2)))) + (type $t2 (func (param (ref $s2)) (result (ref $s1)))) + (func (import "M" "f1") (param (ref $t1))) + (func (import "M" "f1") (param (ref $t2))) + (func (import "M" "f2") (param (ref $t1))) + (func (import "M" "f2") (param (ref $t1))) +) diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 89d3bf9394..41e01585fc 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -146,7 +146,6 @@ fn skip_test(test: &Path, contents: &[u8]) -> bool { "multi-memory/memory_copy1.wast", // the GC proposal isn't implemented yet "gc/gc-array.wat", - "gc/gc-rec-sub.wat", "gc/gc-ref-global-import.wat", "gc/gc-struct.wat", "gc/let.wat", diff --git a/tests/snapshots/local/gc/gc-rec-sub.wat.print b/tests/snapshots/local/gc/gc-rec-sub.wat.print new file mode 100644 index 0000000000..ecb42e6453 --- /dev/null +++ b/tests/snapshots/local/gc/gc-rec-sub.wat.print @@ -0,0 +1,38 @@ +(module + (type (;0;) (func)) + (rec + (type (;1;) (func)) + (type (;2;) (func)) + ) + (type (;3;) (struct)) + (rec + (type (;4;) (struct)) + (type (;5;) (struct)) + ) + (type (;6;) (array i32)) + (rec + (type (;7;) (array i32)) + (type (;8;) (array i32)) + ) + (rec + (type (;9;) (func)) + (type (;10;) (struct)) + (type (;11;) (array i32)) + ) + (rec + (type $a (;12;) (func)) + (type (;13;) (sub $a (;12;) (func))) + ) + (type (;14;) (sub $a (;12;) (func))) + (type (;15;) (sub final $a (;12;) (func))) + (type (;16;) (sub $a (;12;) (func))) + (rec + (type $t1 (;17;) (struct (field (ref 18)))) + (type $t2 (;18;) (struct (field (ref 17)))) + ) + (rec + (type $t3 (;19;) (struct (field (ref 20)))) + (type $t4 (;20;) (sub $t2 (;18;) (struct (field (ref 19))))) + ) + (rec) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/0.print b/tests/snapshots/local/gc/type-equivalence.wast/0.print new file mode 100644 index 0000000000..e6e986b450 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/0.print @@ -0,0 +1,14 @@ +(module + (type $t1 (;0;) (func (param f32 f32) (result f32))) + (type $t2 (;1;) (func (param f32 f32) (result f32))) + (type (;2;) (func (param (ref 0)))) + (type (;3;) (func (param (ref 1)))) + (func $f1 (;0;) (type 2) (param $r (ref 0)) + local.get $r + call $f2 + ) + (func $f2 (;1;) (type 3) (param $r (ref 1)) + local.get $r + call $f1 + ) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/1.print b/tests/snapshots/local/gc/type-equivalence.wast/1.print new file mode 100644 index 0000000000..4677912b11 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/1.print @@ -0,0 +1,17 @@ +(module + (type $s0 (;0;) (func (param i32) (result f32))) + (type $s1 (;1;) (func (param i32 (ref 0)) (result (ref 0)))) + (type $s2 (;2;) (func (param i32 (ref 0)) (result (ref 0)))) + (type $t1 (;3;) (func (param (ref 1)) (result (ref 2)))) + (type $t2 (;4;) (func (param (ref 2)) (result (ref 1)))) + (type (;5;) (func (param (ref 3)))) + (type (;6;) (func (param (ref 4)))) + (func $f1 (;0;) (type 5) (param $r (ref 3)) + local.get $r + call $f2 + ) + (func $f2 (;1;) (type 6) (param $r (ref 4)) + local.get $r + call $f1 + ) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/10.print b/tests/snapshots/local/gc/type-equivalence.wast/10.print new file mode 100644 index 0000000000..ff5f8b66a0 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/10.print @@ -0,0 +1,5 @@ +(module + (type $t2 (;0;) (func (param f32 f32) (result f32))) + (type (;1;) (func (param (ref 0)))) + (import "M" "f" (func (;0;) (type 1))) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/11.print b/tests/snapshots/local/gc/type-equivalence.wast/11.print new file mode 100644 index 0000000000..8dce052a31 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/11.print @@ -0,0 +1,12 @@ +(module + (type $s0 (;0;) (func (param i32) (result f32))) + (type $s1 (;1;) (func (param i32 (ref 0)) (result (ref 0)))) + (type $s2 (;2;) (func (param i32 (ref 0)) (result (ref 0)))) + (type $t1 (;3;) (func (param (ref 1)) (result (ref 2)))) + (type $t2 (;4;) (func (param (ref 2)) (result (ref 1)))) + (type (;5;) (func (param (ref 3)))) + (func (;0;) (type 5) (param (ref 3))) + (func (;1;) (type 5) (param (ref 3))) + (export "f1" (func 0)) + (export "f2" (func 1)) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/13.print b/tests/snapshots/local/gc/type-equivalence.wast/13.print new file mode 100644 index 0000000000..bc2fc84891 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/13.print @@ -0,0 +1,13 @@ +(module + (type $s0 (;0;) (func (param i32) (result f32))) + (type $s1 (;1;) (func (param i32 (ref 0)) (result (ref 0)))) + (type $s2 (;2;) (func (param i32 (ref 0)) (result (ref 0)))) + (type $t1 (;3;) (func (param (ref 1)) (result (ref 2)))) + (type $t2 (;4;) (func (param (ref 2)) (result (ref 1)))) + (type (;5;) (func (param (ref 3)))) + (type (;6;) (func (param (ref 4)))) + (import "M" "f1" (func (;0;) (type 5))) + (import "M" "f1" (func (;1;) (type 6))) + (import "M" "f2" (func (;2;) (type 5))) + (import "M" "f2" (func (;3;) (type 5))) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/2.print b/tests/snapshots/local/gc/type-equivalence.wast/2.print new file mode 100644 index 0000000000..b063e70829 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/2.print @@ -0,0 +1,3 @@ +(module + (type $t (;0;) (func (result (ref 0)))) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/4.print b/tests/snapshots/local/gc/type-equivalence.wast/4.print new file mode 100644 index 0000000000..51cfc60256 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/4.print @@ -0,0 +1,20 @@ +(module + (type $t1 (;0;) (func (param f32 f32))) + (type $t2 (;1;) (func (param f32 f32))) + (type (;2;) (func)) + (func $f1 (;0;) (type $t1) (param f32 f32)) + (func $f2 (;1;) (type $t2) (param f32 f32)) + (func (;2;) (type 2) + f32.const 0x1p+0 (;=1;) + f32.const 0x1p+1 (;=2;) + i32.const 1 + call_indirect (type $t1) + f32.const 0x1p+0 (;=1;) + f32.const 0x1p+1 (;=2;) + i32.const 0 + call_indirect (type $t2) + ) + (table (;0;) 2 2 funcref) + (export "run" (func 2)) + (elem (;0;) (i32.const 0) func $f1 $f2) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/6.print b/tests/snapshots/local/gc/type-equivalence.wast/6.print new file mode 100644 index 0000000000..790198e78d --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/6.print @@ -0,0 +1,41 @@ +(module + (type $s0 (;0;) (func (param i32))) + (type $s1 (;1;) (func (param i32 (ref 0)))) + (type $s2 (;2;) (func (param i32 (ref 0)))) + (type $t1 (;3;) (func (param (ref 1)))) + (type $t2 (;4;) (func (param (ref 2)))) + (type (;5;) (func)) + (func $s1 (;0;) (type $s1) (param i32 (ref 0))) + (func $s2 (;1;) (type $s2) (param i32 (ref 0))) + (func $f1 (;2;) (type $t1) (param (ref 1))) + (func $f2 (;3;) (type $t2) (param (ref 2))) + (func (;4;) (type 5) + ref.func $s1 + i32.const 0 + call_indirect (type $t1) + ref.func $s1 + i32.const 1 + call_indirect (type $t1) + ref.func $s2 + i32.const 0 + call_indirect (type $t1) + ref.func $s2 + i32.const 1 + call_indirect (type $t1) + ref.func $s1 + i32.const 0 + call_indirect (type $t2) + ref.func $s1 + i32.const 1 + call_indirect (type $t2) + ref.func $s2 + i32.const 0 + call_indirect (type $t2) + ref.func $s2 + i32.const 1 + call_indirect (type $t2) + ) + (table (;0;) 4 4 funcref) + (export "run" (func 4)) + (elem (;0;) (i32.const 0) func $f1 $f2 $s1 $s2) +) \ No newline at end of file diff --git a/tests/snapshots/local/gc/type-equivalence.wast/8.print b/tests/snapshots/local/gc/type-equivalence.wast/8.print new file mode 100644 index 0000000000..018898abd6 --- /dev/null +++ b/tests/snapshots/local/gc/type-equivalence.wast/8.print @@ -0,0 +1,6 @@ +(module + (type $t1 (;0;) (func (param f32 f32) (result f32))) + (type (;1;) (func (param (ref 0)))) + (func (;0;) (type 1) (param (ref 0))) + (export "f" (func 0)) +) \ No newline at end of file