diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs index fb87217a5a..a738ceb63c 100644 --- a/crates/wasm-encoder/src/core/code.rs +++ b/crates/wasm-encoder/src/core/code.rs @@ -1316,6 +1316,20 @@ pub enum Instruction<'a> { I64Sub128, I64MulWideS, I64MulWideU, + + RefGetDesc(u32), + RefCastDescNonNull(HeapType), + RefCastDescNullable(HeapType), + BrOnCastDesc { + relative_depth: u32, + from_ref_type: RefType, + to_ref_type: RefType, + }, + BrOnCastDescFail { + relative_depth: u32, + from_ref_type: RefType, + to_ref_type: RefType, + }, } impl Encode for Instruction<'_> { @@ -2144,6 +2158,19 @@ impl Encode for Instruction<'_> { Instruction::I64Sub128 => sink.i64_sub128(), Instruction::I64MulWideS => sink.i64_mul_wide_s(), Instruction::I64MulWideU => sink.i64_mul_wide_u(), + Instruction::RefGetDesc(type_index) => sink.ref_get_desc(type_index), + Instruction::RefCastDescNonNull(hty) => sink.ref_cast_desc_non_null(hty), + Instruction::RefCastDescNullable(hty) => sink.ref_cast_desc_nullable(hty), + Instruction::BrOnCastDesc { + relative_depth, + from_ref_type, + to_ref_type, + } => sink.br_on_cast_desc(relative_depth, from_ref_type, to_ref_type), + Instruction::BrOnCastDescFail { + relative_depth, + from_ref_type, + to_ref_type, + } => sink.br_on_cast_desc_fail(relative_depth, from_ref_type, to_ref_type), }; } } diff --git a/crates/wasm-encoder/src/core/instructions.rs b/crates/wasm-encoder/src/core/instructions.rs index 8ae2fcb784..1f3cc5c66b 100644 --- a/crates/wasm-encoder/src/core/instructions.rs +++ b/crates/wasm-encoder/src/core/instructions.rs @@ -4609,4 +4609,58 @@ impl<'a> InstructionSink<'a> { 22u32.encode(self.sink); self } + + /// Encode [`Instruction::RefGetDesc`]. + pub fn ref_get_desc(&mut self, type_index: u32) -> &mut Self { + self.sink.push(0xFB); + 34u32.encode(self.sink); + type_index.encode(self.sink); + self + } + /// Encode [`Instruction::RefCastDescNonNull`]. + pub fn ref_cast_desc_non_null(&mut self, ht: HeapType) -> &mut Self { + self.sink.push(0xFB); + 35u32.encode(self.sink); + ht.encode(self.sink); + self + } + /// Encode [`Instruction::RefCastDescNullable`]. + pub fn ref_cast_desc_nullable(&mut self, ht: HeapType) -> &mut Self { + self.sink.push(0xFB); + 36u32.encode(self.sink); + ht.encode(self.sink); + self + } + /// Encode [`Instruction::BrOnCastDesc`]. + pub fn br_on_cast_desc( + &mut self, + relative_depth: u32, + from_ref_type: RefType, + to_ref_type: RefType, + ) -> &mut Self { + self.sink.push(0xFB); + 37u32.encode(self.sink); + let cast_flags = (from_ref_type.nullable as u8) | ((to_ref_type.nullable as u8) << 1); + self.sink.push(cast_flags); + relative_depth.encode(self.sink); + from_ref_type.heap_type.encode(self.sink); + to_ref_type.heap_type.encode(self.sink); + self + } + /// Encode [`Instruction::BrOnCastDescFail`]. + pub fn br_on_cast_desc_fail( + &mut self, + relative_depth: u32, + from_ref_type: RefType, + to_ref_type: RefType, + ) -> &mut Self { + self.sink.push(0xFB); + 38u32.encode(self.sink); + let cast_flags = (from_ref_type.nullable as u8) | ((to_ref_type.nullable as u8) << 1); + self.sink.push(cast_flags); + relative_depth.encode(self.sink); + from_ref_type.heap_type.encode(self.sink); + to_ref_type.heap_type.encode(self.sink); + self + } } diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs index a84ffd3fc3..999f8cdf1a 100644 --- a/crates/wasm-encoder/src/core/types.rs +++ b/crates/wasm-encoder/src/core/types.rs @@ -22,6 +22,10 @@ pub struct CompositeType { /// Whether the type is shared. This is part of the /// shared-everything-threads proposal. pub shared: bool, + /// Optional descriptor attribute. + pub descriptor: Option, + /// Optional describes attribute. + pub describes: Option, } /// A [`CompositeType`] can contain one of these types. @@ -362,6 +366,8 @@ pub enum HeapType { /// A concrete Wasm-defined type at the given index. Concrete(u32), + /// An exact type. + Exact(u32), } impl HeapType { @@ -402,6 +408,11 @@ impl Encode for HeapType { // Note that this is encoded as a signed type rather than unsigned // as it's decoded as an s33 HeapType::Concrete(i) => i64::from(*i).encode(sink), + // Exact type is u32 + HeapType::Exact(i) => { + sink.push(0x62); + u32::from(*i).encode(sink) + } } } } @@ -669,6 +680,14 @@ impl<'a> CoreTypeEncoder<'a> { if ty.composite_type.shared { self.bytes.push(0x65); } + if let Some(index) = ty.composite_type.describes { + self.bytes.push(0x4c); + index.encode(self.bytes); + } + if let Some(index) = ty.composite_type.descriptor { + self.bytes.push(0x4d); + index.encode(self.bytes); + } match &ty.composite_type.inner { CompositeInnerType::Func(ty) => { self.encode_function(ty.params().iter().copied(), ty.results().iter().copied()) diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs index 46f87d98bd..c9ba454172 100644 --- a/crates/wasm-encoder/src/reencode.rs +++ b/crates/wasm-encoder/src/reencode.rs @@ -1170,6 +1170,14 @@ pub mod utils { Ok(crate::CompositeType { inner, shared: composite_ty.shared, + descriptor: composite_ty + .descriptor_idx + .map(|i| reencoder.type_index_unpacked(i.unpack())) + .transpose()?, + describes: composite_ty + .describes_idx + .map(|i| reencoder.type_index_unpacked(i.unpack())) + .transpose()?, }) } @@ -1269,6 +1277,9 @@ pub mod utils { wasmparser::HeapType::Concrete(i) => { crate::HeapType::Concrete(reencoder.type_index_unpacked(i)?) } + wasmparser::HeapType::Exact(i) => { + crate::HeapType::Exact(reencoder.type_index_unpacked(i)?) + } wasmparser::HeapType::Abstract { shared, ty } => crate::HeapType::Abstract { shared, ty: reencoder.abstract_heap_type(ty)?, diff --git a/crates/wasm-mutate/src/module.rs b/crates/wasm-mutate/src/module.rs index 0fd6755af7..450d6a9606 100644 --- a/crates/wasm-mutate/src/module.rs +++ b/crates/wasm-mutate/src/module.rs @@ -87,6 +87,7 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result { nullable: ref_ty.is_nullable(), heap_type: match ref_ty.heap_type() { wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().unwrap()), + wasmparser::HeapType::Exact(i) => HeapType::Exact(i.as_module_index().unwrap()), wasmparser::HeapType::Abstract { shared, ty } => { let ty = ty.into(); HeapType::Abstract { shared, ty } diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index e90de0d091..6a4a1dcc21 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -328,6 +328,8 @@ impl From<&CompositeType> for wasm_encoder::CompositeType { wasm_encoder::CompositeType { shared: ty.shared, inner, + descriptor: None, + describes: None, } } } @@ -568,6 +570,10 @@ impl Module { return false; } }, + + (HT::Exact(_), _) => todo!(), + + (_, HT::Exact(_)) => todo!(), } } @@ -892,6 +898,7 @@ impl Module { } } } + HT::Exact(_) => todo!(), } Ok(*u.choose(&choices)?) } @@ -1036,6 +1043,7 @@ impl Module { idx = supertype; } } + HT::Exact(_) => todo!(), } Ok(*u.choose(&choices)?) } @@ -2972,7 +2980,9 @@ impl Module { fn is_shared_ref_type(&self, ty: RefType) -> bool { match ty.heap_type { HeapType::Abstract { shared, .. } => shared, - HeapType::Concrete(i) => self.types[i as usize].composite_type.shared, + HeapType::Concrete(i) | HeapType::Exact(i) => { + self.types[i as usize].composite_type.shared + } } } diff --git a/crates/wasmparser/src/arity.rs b/crates/wasmparser/src/arity.rs index 9eef6fa828..6b3c03e77c 100644 --- a/crates/wasmparser/src/arity.rs +++ b/crates/wasmparser/src/arity.rs @@ -160,8 +160,26 @@ fn visit_call_indirect(module: &dyn ModuleArity, ty: u32, _table: u32) -> Option } fn visit_struct_new(module: &dyn ModuleArity, ty: u32) -> Option<(u32, u32)> { - let (params, _results) = module.sub_type_arity(module.sub_type_at(ty)?)?; - Some((params, 1)) + let ty = module.sub_type_at(ty)?; + let descriptor = if let Some(_) = ty.composite_type.descriptor_idx { + 1 + } else { + 0 + }; + let (params, _results) = module.sub_type_arity(ty)?; + Some((params + descriptor, 1)) +} + +fn visit_struct_new_default(module: &dyn ModuleArity, ty: u32) -> Option<(u32, u32)> { + let ty = module.sub_type_at(ty)?; + Some(( + if let Some(_) = ty.composite_type.descriptor_idx { + 1 + } else { + 0 + }, + 1, + )) } fn visit_array_new_fixed(_module: &dyn ModuleArity, _ty: u32, size: u32) -> Option<(u32, u32)> { @@ -289,3 +307,23 @@ fn visit_switch(module: &dyn ModuleArity, cont: u32, _tag: u32) -> Option<(u32, module.sub_type_arity(module.sub_type_of_ref_type(&last_param.as_reference_type()?)?)?; Some((params, cont_params)) } + +fn visit_br_on_cast_desc( + module: &dyn ModuleArity, + depth: u32, + _from: RefType, + _to: RefType, +) -> Option<(u32, u32)> { + let (params, _) = visit_br(module, depth)?; + Some((params + 1, params)) +} + +fn visit_br_on_cast_desc_fail( + module: &dyn ModuleArity, + depth: u32, + _from: RefType, + _to: RefType, +) -> Option<(u32, u32)> { + let (params, _) = visit_br(module, depth)?; + Some((params + 1, params)) +} diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index 611d8ee98f..d70849e074 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -1362,6 +1362,56 @@ impl<'a> BinaryReader<'a> { 0x1d => visitor.visit_i31_get_s(), 0x1e => visitor.visit_i31_get_u(), + 0x22 => visitor.visit_ref_get_desc(self.read()?), + 0x23 => visitor.visit_ref_cast_desc_non_null(self.read()?), + 0x24 => visitor.visit_ref_cast_desc_nullable(self.read()?), + 0x25 => { + let pos = self.original_position(); + let cast_flags = self.read_u8()?; + let relative_depth = self.read_var_u32()?; + let (from_type_nullable, to_type_nullable) = match cast_flags { + 0b00 => (false, false), + 0b01 => (true, false), + 0b10 => (false, true), + 0b11 => (true, true), + _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), + }; + let from_heap_type = self.read()?; + let from_ref_type = + RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + let to_heap_type = self.read()?; + let to_ref_type = + RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + visitor.visit_br_on_cast_desc(relative_depth, from_ref_type, to_ref_type) + } + 0x26 => { + let pos = self.original_position(); + let cast_flags = self.read_u8()?; + let relative_depth = self.read_var_u32()?; + let (from_type_nullable, to_type_nullable) = match cast_flags { + 0 => (false, false), + 1 => (true, false), + 2 => (false, true), + 3 => (true, true), + _ => bail!(pos, "invalid cast flags: {cast_flags:08b}"), + }; + let from_heap_type = self.read()?; + let from_ref_type = + RefType::new(from_type_nullable, from_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + let to_heap_type = self.read()?; + let to_ref_type = + RefType::new(to_type_nullable, to_heap_type).ok_or_else(|| { + format_err!(pos, "implementation error: type index too large") + })?; + visitor.visit_br_on_cast_desc_fail(relative_depth, from_ref_type, to_ref_type) + } + _ => bail!(pos, "unknown 0xfb subopcode: 0x{code:x}"), }) } diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs index 2ef63ac5a5..4f9b1774aa 100644 --- a/crates/wasmparser/src/features.rs +++ b/crates/wasmparser/src/features.rs @@ -295,6 +295,9 @@ define_wasm_features! { /// /// This is a subcomponent of the "lime1" feature. pub bulk_memory_opt: BULK_MEMORY_OPT(1 << 34) = true; + + // Custom descriptors proposal. + pub custom_descriptors: CUSTOM_DESCRIPTORS(1 << 35) = false; } } diff --git a/crates/wasmparser/src/lib.rs b/crates/wasmparser/src/lib.rs index 48855f5e3e..59a918e264 100644 --- a/crates/wasmparser/src/lib.rs +++ b/crates/wasmparser/src/lib.rs @@ -259,7 +259,7 @@ macro_rules! _for_each_operator_group { @gc { RefEq => visit_ref_eq (arity 2 -> 1) StructNew { struct_type_index: u32 } => visit_struct_new (arity custom) - StructNewDefault { struct_type_index: u32 } => visit_struct_new_default (arity 0 -> 1) + StructNewDefault { struct_type_index: u32 } => visit_struct_new_default (arity custom) StructGet { struct_type_index: u32, field_index: u32 } => visit_struct_get (arity 1 -> 1) StructGetS { struct_type_index: u32, field_index: u32 } => visit_struct_get_s (arity 1 -> 1) StructGetU { struct_type_index: u32, field_index: u32 } => visit_struct_get_u (arity 1 -> 1) @@ -297,6 +297,20 @@ macro_rules! _for_each_operator_group { RefI31 => visit_ref_i31 (arity 1 -> 1) I31GetS => visit_i31_get_s (arity 1 -> 1) I31GetU => visit_i31_get_u (arity 1 -> 1) + + RefGetDesc { type_index: u32 } => visit_ref_get_desc (arity 1 -> 1) + RefCastDescNonNull { hty: $crate::HeapType } => visit_ref_cast_desc_non_null (arity 2 -> 1) + RefCastDescNullable { hty: $crate::HeapType } => visit_ref_cast_desc_nullable (arity 2 -> 1) + BrOnCastDesc { + relative_depth: u32, + from_ref_type: $crate::RefType, + to_ref_type: $crate::RefType + } => visit_br_on_cast_desc (arity custom) + BrOnCastDescFail { + relative_depth: u32, + from_ref_type: $crate::RefType, + to_ref_type: $crate::RefType + } => visit_br_on_cast_desc_fail (arity custom) } // 0xFC operators diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs index 5b35dccbf9..caab440ee7 100644 --- a/crates/wasmparser/src/readers/core/types.rs +++ b/crates/wasmparser/src/readers/core/types.rs @@ -47,10 +47,10 @@ pub(crate) use self::matches::{Matches, WithRecGroup}; // // This is a bit-packed `u32` with the following layout: // -// [ unused:u10 kind:u2 index:u20 ] +// [ unused:u11 kind:u2 index:u19 ] // -// It must fit in 22 bits to keep `RefType` in 24 bits and `ValType` in 32 bits, -// so the top ten bits are unused. +// It must fit in 21 bits to keep `RefType` in 24 bits and `ValType` in 32 bits, +// so the top 11 bits are unused. // // The `index` field's interpretation depends on the `kind` field, which may be // one of the following: @@ -83,13 +83,13 @@ fn can_fit_max_wasm_types_in_packed_index() { impl PackedIndex { const UNUSED_MASK: u32 = u32::MAX & !(Self::KIND_MASK | Self::INDEX_MASK); - const KIND_MASK: u32 = 0b11 << 20; - const INDEX_MASK: u32 = (1 << 20) - 1; + const KIND_MASK: u32 = 0b11 << 19; + const INDEX_MASK: u32 = (1 << 19) - 1; - const MODULE_KIND: u32 = 0b00 << 20; - const REC_GROUP_KIND: u32 = 0b01 << 20; + const MODULE_KIND: u32 = 0b00 << 19; + const REC_GROUP_KIND: u32 = 0b01 << 19; #[cfg(feature = "validate")] - const ID_KIND: u32 = 0b10 << 20; + const ID_KIND: u32 = 0b10 << 19; #[inline] pub(crate) fn unchecked_from_u32(x: u32) -> Self { @@ -489,6 +489,8 @@ impl SubType { composite_type: CompositeType { inner: CompositeInnerType::Func(signature), shared, + descriptor_idx: None, + describes_idx: None, }, } } @@ -523,6 +525,12 @@ impl SubType { if let Some(idx) = &mut self.supertype_idx { f(idx)?; } + if let Some(idx) = &mut self.composite_type.descriptor_idx { + f(idx)?; + } + if let Some(idx) = &mut self.composite_type.describes_idx { + f(idx)?; + } match &mut self.composite_type.inner { CompositeInnerType::Func(ty) => { for ty in ty.params_mut() { @@ -580,6 +588,10 @@ pub struct CompositeType { /// Is the composite type shared? This is part of the /// shared-everything-threads proposal. pub shared: bool, + /// The descriptor type. + pub descriptor_idx: Option, + /// The descriptor for type. + pub describes_idx: Option, } impl fmt::Display for CompositeType { @@ -936,7 +948,11 @@ impl ValType { ValType::Ref(r) => { if let Some(mut idx) = r.type_index() { map(&mut idx)?; - *r = RefType::concrete(r.is_nullable(), idx); + *r = if r.is_exact_type_ref() { + RefType::exact(r.is_nullable(), idx) + } else { + RefType::concrete(r.is_nullable(), idx) + } } } ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} @@ -1032,6 +1048,9 @@ impl fmt::Debug for RefType { write!(f, "(ref {index})") } } + HeapType::Exact(index) => { + write!(f, "(ref (exact {index}))") + } } } } @@ -1087,7 +1106,8 @@ impl RefType { const NOCONT_ABSTYPE: u32 = 0b0110 << 17; // The `index` is valid only when `concrete == 1`. - const INDEX_MASK: u32 = (1 << 22) - 1; + const EXACT_BIT: u32 = 1 << 21; + const INDEX_MASK: u32 = (1 << 21) - 1; /// A nullable untyped function reference aka `(ref null func)` aka /// `funcref` aka `anyfunc`. @@ -1243,6 +1263,14 @@ impl RefType { RefType::from_u32(nullable32 | Self::CONCRETE_BIT | index) } + /// Create a reference to exact type. + pub fn exact(nullable: bool, index: PackedIndex) -> Self { + let index: u32 = PackedIndex::to_u32(index); + debug_assert!(Self::can_represent_type_index(index)); + let nullable32 = Self::NULLABLE_BIT * nullable as u32; + RefType::from_u32(nullable32 | Self::EXACT_BIT | Self::CONCRETE_BIT | index) + } + /// Create a new `RefType`. /// /// Returns `None` when the heap type's type index (if any) is beyond this @@ -1251,6 +1279,7 @@ impl RefType { let base32 = Self::NULLABLE_BIT * (nullable as u32); match heap_type { HeapType::Concrete(index) => Some(RefType::concrete(nullable, index.pack()?)), + HeapType::Exact(index) => Some(RefType::exact(nullable, index.pack()?)), HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; let base32 = base32 | (Self::SHARED_BIT * (shared as u32)); @@ -1294,6 +1323,11 @@ impl RefType { self.as_u32() & Self::CONCRETE_BIT != 0 } + /// Is this an exact reference to a type? + pub const fn is_exact_type_ref(&self) -> bool { + !self.as_u32() & (Self::EXACT_BIT | Self::CONCRETE_BIT) == 0 + } + /// If this is a reference to a concrete Wasm-defined type, get its /// type index. pub fn type_index(&self) -> Option { @@ -1368,7 +1402,11 @@ impl RefType { pub fn heap_type(&self) -> HeapType { let s = self.as_u32(); if self.is_concrete_type_ref() { - HeapType::Concrete(self.type_index().unwrap().unpack()) + if !self.is_exact_type_ref() { + HeapType::Concrete(self.type_index().unwrap().unpack()) + } else { + HeapType::Exact(self.type_index().unwrap().unpack()) + } } else { use AbstractHeapType::*; let shared = s & Self::SHARED_BIT != 0; @@ -1471,6 +1509,13 @@ impl RefType { "(ref $type)" } } + HeapType::Exact(_) => { + if nullable { + "(ref null (exact $type))" + } else { + "(ref (exact $type))" + } + } } } } @@ -1491,6 +1536,10 @@ pub enum HeapType { /// /// Introduced in the function-references proposal. Concrete(UnpackedIndex), + /// An exact, user-defined type. + /// + /// Introduced in the custom-descriptors proposal. + Exact(UnpackedIndex), } impl HeapType { @@ -1712,6 +1761,7 @@ impl<'a> FromReader<'a> for ValType { // | 0x65 | -27 | shared $t | shared-everything proposal | // | 0x64 | -28 | ref $t | gc proposal, prefix byte | // | 0x63 | -29 | ref null $t | gc proposal, prefix byte | + // | 0x62 | -30 | exact $t | custom descriptor proposal | // | 0x60 | -32 | func $t | prefix byte | // | 0x5f | -33 | struct $t | gc proposal, prefix byte | // | 0x5e | -34 | array $t | gc proposal, prefix byte | @@ -1820,6 +1870,11 @@ impl<'a> FromReader<'a> for HeapType { let ty = reader.read()?; Ok(HeapType::Abstract { shared: true, ty }) } + 0x62 => { + reader.read_u8()?; + let idx = reader.read_var_u32()?; + Ok(HeapType::Exact(UnpackedIndex::Module(idx))) + } _ => { // Reclassify errors as "invalid heap type" here because // that's the "root" of what was being parsed rather than @@ -2027,6 +2082,28 @@ fn read_composite_type( } else { (false, opcode) }; + let (describes_idx, opcode) = if opcode == 0x4c { + let idx = PackedIndex::from_module_index(reader.read_var_u32()?).ok_or_else(|| { + BinaryReaderError::new( + "type index greater than implementation limits", + reader.original_position(), + ) + })?; + (Some(idx), reader.read_u8()?) + } else { + (None, opcode) + }; + let (descriptor_idx, opcode) = if opcode == 0x4d { + let idx = PackedIndex::from_module_index(reader.read_var_u32()?).ok_or_else(|| { + BinaryReaderError::new( + "type index greater than implementation limits", + reader.original_position(), + ) + })?; + (Some(idx), reader.read_u8()?) + } else { + (None, opcode) + }; let inner = match opcode { 0x60 => CompositeInnerType::Func(reader.read()?), 0x5e => CompositeInnerType::Array(reader.read()?), @@ -2034,7 +2111,12 @@ fn read_composite_type( 0x5d => CompositeInnerType::Cont(reader.read()?), x => return reader.invalid_leading_byte(x, "type"), }; - Ok(CompositeType { shared, inner }) + Ok(CompositeType { + shared, + inner, + descriptor_idx, + describes_idx, + }) } impl<'a> FromReader<'a> for RecGroup { diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index a5c345e895..255398f4dc 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -277,6 +277,16 @@ impl WasmFeatures { Err("function references required for index reference types") } } + HeapType::Exact(_) => { + // The exact indexed types require the function-references, + // gc, or custom descriptors proposal. + // See also the comment above for HeapType::Concrete(_). + if self.function_references() || self.gc() || self.custom_descriptors() { + Ok(()) + } else { + Err("custom descriptors required for exact reference types") + } + } HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; if shared && !self.shared_everything_threads() { diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs index 64ddf1ccae..8b474ec73a 100644 --- a/crates/wasmparser/src/validator/component_types.rs +++ b/crates/wasmparser/src/validator/component_types.rs @@ -1159,7 +1159,7 @@ impl ArgOrField { pub(crate) fn as_concrete_ref(self) -> Option { match self.as_ref_type()?.heap_type() { HeapType::Abstract { .. } => None, - HeapType::Concrete(idx) => { + HeapType::Concrete(idx) | HeapType::Exact(idx) => { let id = idx .as_core_type_id() .expect("validation only sees core type ids"); diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index ab38a0daf4..f8a4e2df41 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -838,7 +838,7 @@ impl Module { // Check that the heap type is valid. let type_index = match ty { HeapType::Abstract { .. } => return Ok(()), - HeapType::Concrete(type_index) => type_index, + HeapType::Concrete(type_index) | HeapType::Exact(type_index) => type_index, }; match type_index { UnpackedIndex::Module(idx) => { diff --git a/crates/wasmparser/src/validator/core/canonical.rs b/crates/wasmparser/src/validator/core/canonical.rs index 89f78da70a..a269f635db 100644 --- a/crates/wasmparser/src/validator/core/canonical.rs +++ b/crates/wasmparser/src/validator/core/canonical.rs @@ -115,6 +115,7 @@ pub(crate) trait InternRecGroup { self.add_type_id(id); if is_new { self.check_subtype(rec_group_id, id, types, offset)?; + self.check_descriptors(rec_group_id, id, types, offset)?; } } @@ -162,6 +163,111 @@ pub(crate) trait InternRecGroup { Ok(()) } + fn check_descriptors( + &mut self, + rec_group: RecGroupId, + id: CoreTypeId, + types: &TypeList, + offset: usize, + ) -> Result<()> { + let ty = &types[id].composite_type; + if ty.descriptor_idx.is_some() || ty.describes_idx.is_some() { + if !self.features().custom_descriptors() { + return Err(BinaryReaderError::new( + "custom descriptors proposal must be enabled to use descriptor and describes", + offset, + )); + } + match &ty.inner { + CompositeInnerType::Struct(_) => (), + _ => { + return Err(BinaryReaderError::new( + if ty.descriptor_idx.is_some() { + "descriptor clause on non-struct type" + } else { + "describes clause on non-struct type" + }, + offset, + )); + } + } + } + + let map_cannonical = |idx: PackedIndex| -> Result { + self.at_packed_index(types, rec_group, idx, offset) + }; + + let descriptor_idx = if let Some(i) = ty.descriptor_idx { + Some(map_cannonical(i)?) + } else { + None + }; + let describes_idx = if let Some(i) = ty.describes_idx { + Some(map_cannonical(i)?) + } else { + None + }; + + if let Some(supertype_index) = types[id].supertype_idx { + debug_assert!(supertype_index.is_canonical()); + let sup_id = map_cannonical(supertype_index)?; + if let Some(descriptor_idx) = descriptor_idx { + if types[sup_id].composite_type.descriptor_idx.is_some() + && (types[descriptor_idx].supertype_idx.is_none() + || (map_cannonical(types[descriptor_idx].supertype_idx.unwrap())? + != map_cannonical( + types[sup_id].composite_type.descriptor_idx.unwrap(), + )?)) + { + bail!( + offset, + "supertype of described type must be described by supertype of descriptor", + ); + } + } else if types[sup_id].composite_type.descriptor_idx.is_some() { + bail!( + offset, + "supertype of type without descriptor cannot have descriptor", + ); + } + if types[id].composite_type.describes_idx.is_some() + != types[sup_id].composite_type.describes_idx.is_some() + { + if types[id].composite_type.describes_idx.is_none() { + bail!( + offset, + "supertype of non-descriptor type cannot be a descriptor" + ); + } else { + bail!(offset, "supertype of descriptor must be a descriptor"); + } + } + } + if let Some(descriptor_idx) = descriptor_idx { + let describes_idx = if let Some(i) = types[descriptor_idx].composite_type.describes_idx + { + Some(map_cannonical(i)?) + } else { + None + }; + if describes_idx.is_none() || id != describes_idx.unwrap() { + bail!(offset, "descriptor with no matching describes",); + } + } + if let Some(describes_idx) = describes_idx { + let descriptor_idx = if let Some(i) = types[describes_idx].composite_type.descriptor_idx + { + Some(map_cannonical(i)?) + } else { + None + }; + if descriptor_idx.is_none() || id != descriptor_idx.unwrap() { + bail!(offset, "describes with no matching descriptor",); + } + } + Ok(()) + } + fn check_composite_type( &mut self, ty: &CompositeType, @@ -348,6 +454,12 @@ impl<'a> TypeCanonicalizer<'a> { } } + if let Some(idx) = ty.composite_type.describes_idx.as_mut() { + if idx.as_module_index().map_or(false, |i| i >= type_index) { + bail!(self.offset, "forward describes reference"); + } + } + ty.remap_indices(&mut |idx| self.canonicalize_type_index(idx))?; } diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index 65cb9dfa2e..181c72e756 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -620,6 +620,20 @@ where self.push_operand(ref_ty) } + fn push_exact_ref(&mut self, nullable: bool, type_index: u32) -> Result<()> { + let mut heap_ty = HeapType::Exact(UnpackedIndex::Module(type_index)); + + // Canonicalize the module index into an id. + self.resources.check_heap_type(&mut heap_ty, self.offset)?; + debug_assert!(matches!(heap_ty, HeapType::Exact(UnpackedIndex::Id(_)))); + + let ref_ty = RefType::new(nullable, heap_ty).ok_or_else(|| { + format_err!(self.offset, "implementation limit: type index too large") + })?; + + self.push_operand(ref_ty) + } + fn pop_concrete_ref(&mut self, nullable: bool, type_index: u32) -> Result { let mut heap_ty = HeapType::Concrete(UnpackedIndex::Module(type_index)); @@ -634,6 +648,23 @@ where self.pop_operand(Some(ref_ty.into())) } + fn maybe_exact_type(&self, ty: MaybeType) -> bool { + match ty { + MaybeType::Known(ValType::Ref(rt)) if rt.is_exact_type_ref() => true, + MaybeType::Bottom => true, + _ => false, + } + } + + fn pop_concrete_or_exact_ref( + &mut self, + nullable: bool, + type_index: u32, + ) -> Result<(MaybeType, bool)> { + let ty = self.pop_concrete_ref(nullable, type_index)?; + Ok((ty, self.maybe_exact_type(ty))) + } + /// Pop the given label types, checking that they are indeed present on the /// stack, and then push them back on again. fn pop_push_label_types( @@ -1291,6 +1322,101 @@ where self.push_operand(sub_ty) } + /// Common helper to check type hierarchy for `br_on_cast` operators. + fn check_br_on_cast_type_hierarchy( + &self, + from_ref_type: RefType, + to_ref_type: RefType, + ) -> Result<()> { + if self.features.custom_descriptors() { + // The constraint C |- rt_2 <: rt_1 on branching cast instructions + // before the custom descriptors proposal is relaxed to the constraint + // that rt_1 and rt_2 share some arbitrary valid supertype rt', i.e. + // that rt_1 and rt_2 must be in the same heap type hierarchy. + let from_ref_type_top = self.resources.top_type(&from_ref_type.heap_type()); + let to_ref_type_top = self.resources.top_type(&to_ref_type.heap_type()); + if from_ref_type_top != to_ref_type_top { + bail!( + self.offset, + "type mismatch: {from_ref_type} and {to_ref_type} have different heap type hierarchies" + ); + } + return Ok(()); + } + + if !self + .resources + .is_subtype(to_ref_type.into(), from_ref_type.into()) + { + bail!( + self.offset, + "type mismatch: expected {from_ref_type}, found {to_ref_type}" + ); + } + Ok(()) + } + + /// Common helper to check descriptor for the specified type. + fn check_descriptor(&self, heap_type: HeapType) -> Result { + Ok(match heap_type { + HeapType::Exact(idx) | HeapType::Concrete(idx) => { + if let Some(descriptor_idx) = self + .sub_type_at(idx.as_module_index().unwrap())? + .composite_type + .descriptor_idx + { + u32::try_from(crate::validator::types::TypeIdentifier::index( + &descriptor_idx.as_core_type_id().unwrap(), + )) + .unwrap() + } else { + bail!(self.offset, "cast target must have descriptor") + } + } + _ => bail!(self.offset, "unexpected heap type"), + }) + } + + /// Common helper for both nullable and non-nullable variants of `ref.cast_desc` + /// instructions. + fn check_ref_cast_desc(&mut self, nullable: bool, heap_type: HeapType) -> Result<()> { + let descriptor_idx = self.check_descriptor(heap_type)?; + let (_, is_exact) = self.pop_concrete_or_exact_ref(true, descriptor_idx)?; + if let HeapType::Exact(_) = heap_type { + if !is_exact { + bail!( + self.offset, + "type mismatch: expected exact type because the descriptor is exact" + ); + } + } + + self.check_downcast(nullable, heap_type)?; + + let idx = { + let mut heap_type = heap_type; + self.resources + .check_heap_type(&mut heap_type, self.offset)?; + match heap_type { + HeapType::Concrete(index) | HeapType::Exact(index) => { + index.pack().ok_or_else(|| { + BinaryReaderError::new( + "implementation limit: type index too large", + self.offset, + ) + })? + } + _ => panic!(), + } + }; + + self.push_operand(if is_exact { + RefType::exact(nullable, idx) + } else { + RefType::concrete(nullable, idx) + }) + } + /// Common helper for checking the types of globals accessed with atomic RMW /// instructions, which only allow `i32` and `i64`. fn check_atomic_global_rmw_ty(&self, global_index: u32) -> Result { @@ -3275,14 +3401,30 @@ where Ok(()) } fn visit_struct_new(&mut self, struct_type_index: u32) -> Self::Output { + if let Some(descriptor_idx) = self + .sub_type_at(struct_type_index)? + .composite_type + .descriptor_idx + { + let ty = ValType::Ref(RefType::exact(true, descriptor_idx)); + self.pop_operand(Some(ty))?; + } let struct_ty = self.struct_type_at(struct_type_index)?; for ty in struct_ty.fields.iter().rev() { self.pop_operand(Some(ty.element_type.unpack()))?; } - self.push_concrete_ref(false, struct_type_index)?; + if self.features.custom_descriptors() { + self.push_exact_ref(false, struct_type_index)?; + } else { + self.push_concrete_ref(false, struct_type_index)?; + } Ok(()) } fn visit_struct_new_default(&mut self, type_index: u32) -> Self::Output { + if let Some(descriptor_idx) = self.sub_type_at(type_index)?.composite_type.descriptor_idx { + let ty = ValType::Ref(RefType::exact(true, descriptor_idx)); + self.pop_operand(Some(ty))?; + } let ty = self.struct_type_at(type_index)?; for field in ty.fields.iter() { let val_ty = field.element_type.unpack(); @@ -3293,7 +3435,11 @@ where ); } } - self.push_concrete_ref(false, type_index)?; + if self.features.custom_descriptors() { + self.push_exact_ref(false, type_index)?; + } else { + self.push_concrete_ref(false, type_index)?; + } Ok(()) } fn visit_struct_get(&mut self, struct_type_index: u32, field_index: u32) -> Self::Output { @@ -3897,15 +4043,7 @@ where self.resources .check_ref_type(&mut to_ref_type, self.offset)?; - if !self - .resources - .is_subtype(to_ref_type.into(), from_ref_type.into()) - { - bail!( - self.offset, - "type mismatch: expected {from_ref_type}, found {to_ref_type}" - ); - } + self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?; let (block_ty, frame_kind) = self.jump(relative_depth)?; let mut label_types = self.label_types(block_ty, frame_kind)?; @@ -3941,15 +4079,7 @@ where self.resources .check_ref_type(&mut to_ref_type, self.offset)?; - if !self - .resources - .is_subtype(to_ref_type.into(), from_ref_type.into()) - { - bail!( - self.offset, - "type mismatch: expected {from_ref_type}, found {to_ref_type}" - ); - } + self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?; let (block_ty, frame_kind) = self.jump(relative_depth)?; let mut label_tys = self.label_types(block_ty, frame_kind)?; @@ -4231,6 +4361,119 @@ where fn visit_i64_mul_wide_u(&mut self) -> Result<()> { self.check_i64_mul_wide() } + + fn visit_ref_get_desc(&mut self, type_index: u32) -> Self::Output { + let (_ty, is_exact) = self.pop_concrete_or_exact_ref(true, type_index)?; + match self.sub_type_at(type_index)?.composite_type.descriptor_idx { + Some(descriptor_idx) => { + let ref_ty = if is_exact { + RefType::exact(false, descriptor_idx) + } else { + RefType::concrete(false, descriptor_idx) + }; + self.push_operand(ref_ty) + } + None => bail!(self.offset, "expected type with descriptor"), + } + } + + fn visit_ref_cast_desc_non_null(&mut self, heap_type: HeapType) -> Self::Output { + self.check_ref_cast_desc(false, heap_type) + } + fn visit_ref_cast_desc_nullable(&mut self, heap_type: HeapType) -> Self::Output { + self.check_ref_cast_desc(true, heap_type) + } + fn visit_br_on_cast_desc( + &mut self, + relative_depth: u32, + mut from_ref_type: RefType, + mut to_ref_type: RefType, + ) -> Self::Output { + let descriptor_idx = self.check_descriptor(to_ref_type.heap_type())?; + + self.resources + .check_ref_type(&mut from_ref_type, self.offset)?; + self.resources + .check_ref_type(&mut to_ref_type, self.offset)?; + + self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?; + + let (_, is_exact) = self.pop_concrete_or_exact_ref(true, descriptor_idx)?; + if to_ref_type.is_exact_type_ref() && !is_exact { + bail!( + self.offset, + "type mismatch: expected exact type because the descriptor is exact" + ); + } + + let (block_ty, frame_kind) = self.jump(relative_depth)?; + let mut label_types = self.label_types(block_ty, frame_kind)?; + + match label_types.next_back() { + Some(label_ty) if self.resources.is_subtype(to_ref_type.into(), label_ty) => { + self.pop_operand(Some(from_ref_type.into()))?; + } + Some(label_ty) => bail!( + self.offset, + "type mismatch: casting to type {to_ref_type}, but it does not match \ + label result type {label_ty}" + ), + None => bail!( + self.offset, + "type mismatch: br_on_cast to label with empty types, must have a reference type" + ), + }; + + self.pop_push_label_types(label_types)?; + let diff_ty = RefType::difference(from_ref_type, to_ref_type); + self.push_operand(diff_ty)?; + Ok(()) + } + fn visit_br_on_cast_desc_fail( + &mut self, + relative_depth: u32, + mut from_ref_type: RefType, + mut to_ref_type: RefType, + ) -> Self::Output { + let descriptor_idx = self.check_descriptor(to_ref_type.heap_type())?; + + self.resources + .check_ref_type(&mut from_ref_type, self.offset)?; + self.resources + .check_ref_type(&mut to_ref_type, self.offset)?; + + self.check_br_on_cast_type_hierarchy(from_ref_type, to_ref_type)?; + + let (_, is_exact) = self.pop_concrete_or_exact_ref(true, descriptor_idx)?; + if to_ref_type.is_exact_type_ref() && !is_exact { + bail!( + self.offset, + "type mismatch: expected exact type because the descriptor is exact" + ); + } + + let (block_ty, frame_kind) = self.jump(relative_depth)?; + let mut label_tys = self.label_types(block_ty, frame_kind)?; + + let diff_ty = RefType::difference(from_ref_type, to_ref_type); + match label_tys.next_back() { + Some(label_ty) if self.resources.is_subtype(diff_ty.into(), label_ty) => { + self.pop_operand(Some(from_ref_type.into()))?; + } + Some(label_ty) => bail!( + self.offset, + "type mismatch: expected label result type {label_ty}, found {diff_ty}" + ), + None => bail!( + self.offset, + "type mismatch: expected a reference type, found nothing" + ), + } + + self.pop_push_label_types(label_tys)?; + self.push_operand(to_ref_type)?; + Ok(()) + } } #[derive(Clone, Debug)] diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs index c072218caa..738f3f9cad 100644 --- a/crates/wasmparser/src/validator/types.rs +++ b/crates/wasmparser/src/validator/types.rs @@ -1136,7 +1136,8 @@ impl TypeList { }, ) => a_shared == b_shared && a_ty.is_subtype_of(b_ty), - (HT::Concrete(a), HT::Abstract { shared, ty }) => { + (HT::Concrete(a), HT::Abstract { shared, ty }) + | (HT::Exact(a), HT::Abstract { shared, ty }) => { let a_ty = &subtype(a_group, a).composite_type; if a_ty.shared != shared { return false; @@ -1153,7 +1154,8 @@ impl TypeList { } } - (HT::Abstract { shared, ty }, HT::Concrete(b)) => { + (HT::Abstract { shared, ty }, HT::Concrete(b)) + | (HT::Abstract { shared, ty }, HT::Exact(b)) => { let b_ty = &subtype(b_group, b).composite_type; if shared != b_ty.shared { return false; @@ -1169,9 +1171,13 @@ impl TypeList { } } - (HT::Concrete(a), HT::Concrete(b)) => { + (HT::Concrete(a), HT::Concrete(b)) | (HT::Exact(a), HT::Concrete(b)) => { self.id_is_subtype(core_type_id(a_group, a), core_type_id(b_group, b)) } + + (HT::Exact(a), HT::Exact(b)) => core_type_id(a_group, a) == core_type_id(b_group, b), + + (HT::Concrete(_), HT::Exact(_)) => false, } } @@ -1206,7 +1212,7 @@ impl TypeList { pub fn reftype_is_shared(&self, ty: RefType) -> bool { match ty.heap_type() { HeapType::Abstract { shared, .. } => shared, - HeapType::Concrete(index) => { + HeapType::Concrete(index) | HeapType::Exact(index) => { self[index.as_core_type_id().unwrap()].composite_type.shared } } @@ -1219,7 +1225,7 @@ impl TypeList { pub fn top_type(&self, heap_type: &HeapType) -> HeapType { use AbstractHeapType::*; match *heap_type { - HeapType::Concrete(idx) => { + HeapType::Concrete(idx) | HeapType::Exact(idx) => { let ty = &self[idx.as_core_type_id().unwrap()].composite_type; let shared = ty.shared; match ty.inner { diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index 889c7797ac..b04a3ae2d4 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -925,6 +925,18 @@ impl Printer<'_, '_> { self.start_group("shared")?; self.result.write_str(" ")?; } + if let Some(idx) = ty.describes_idx { + self.start_group("describes")?; + self.result.write_str(" ")?; + self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?; + self.result.write_str(" ")?; + } + if let Some(idx) = ty.descriptor_idx { + self.start_group("descriptor")?; + self.result.write_str(" ")?; + self.print_idx(&state.core.type_names, idx.as_module_index().unwrap())?; + self.result.write_str(" ")?; + } let r = match &ty.inner { CompositeInnerType::Func(ty) => { self.start_group("func")?; @@ -951,6 +963,12 @@ impl Printer<'_, '_> { r } }; + if ty.descriptor_idx.is_some() { + self.end_group()?; // `descriptor` + } + if ty.describes_idx.is_some() { + self.end_group()?; // `describes` + } if ty.shared { self.end_group()?; // `shared` } @@ -980,6 +998,8 @@ impl Printer<'_, '_> { CompositeType { inner: CompositeInnerType::Func(ty), shared: false, + descriptor_idx: None, + describes_idx: None, }, .. })) => self.print_func_type(state, ty, names_for).map(Some), @@ -1143,6 +1163,11 @@ impl Printer<'_, '_> { HeapType::Concrete(i) => { self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?; } + HeapType::Exact(i) => { + self.start_group("exact ")?; + self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?; + self.end_group()?; + } HeapType::Abstract { shared, ty } => { use AbstractHeapType::*; if shared { diff --git a/crates/wasmprinter/src/operator.rs b/crates/wasmprinter/src/operator.rs index 35cdc85256..466a40b38e 100644 --- a/crates/wasmprinter/src/operator.rs +++ b/crates/wasmprinter/src/operator.rs @@ -765,6 +765,23 @@ macro_rules! define_visit { $self.push_str(" ")?; $self.printer.print_field_idx($self.state, $ty, $field)?; ); + + (payload $self:ident RefGetDesc $hty:ident) => ( + $self.struct_type_index($hty)?; + ); + (payload $self:ident RefCastDescNonNull $hty:ident) => ( + $self.push_str(" ")?; + let rty = RefType::new(false, $hty) + .ok_or_else(|| anyhow!("implementation limit: type index too large"))?; + $self.printer.print_reftype($self.state, rty)?; + ); + (payload $self:ident RefCastDescNullable $hty:ident) => ( + $self.push_str(" ")?; + let rty = RefType::new(true, $hty) + .ok_or_else(|| anyhow!("implementation limit: type index too large"))?; + $self.printer.print_reftype($self.state, rty)?; + ); + (payload $self:ident $op:ident $($arg:ident)*) => ( $($self.$arg($arg)?;)* ); @@ -1388,6 +1405,11 @@ macro_rules! define_visit { (name I64Sub128) => ("i64.sub128"); (name I64MulWideS) => ("i64.mul_wide_s"); (name I64MulWideU) => ("i64.mul_wide_u"); + (name RefGetDesc) => ("ref.get_desc"); + (name RefCastDescNonNull) => ("ref.cast_desc"); + (name RefCastDescNullable) => ("ref.cast_desc"); + (name BrOnCastDesc) => ("br_on_cast_desc"); + (name BrOnCastDescFail) => ("br_on_cast_desc_fail"); } impl<'a> VisitOperator<'a> for PrintOperator<'_, '_, '_, '_> { diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index 54e6f3e272..beebb2f2b0 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -661,7 +661,7 @@ impl<'a> Resolver<'a> { ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {} ValType::Ref(r) => match &mut r.heap { core::HeapType::Abstract { .. } => {} - core::HeapType::Concrete(id) => { + core::HeapType::Concrete(id) | core::HeapType::Exact(id) => { self.resolve_ns(id, Ns::Type)?; } }, diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs index cc27b92c99..09b491b83c 100644 --- a/crates/wast/src/core/binary.rs +++ b/crates/wast/src/core/binary.rs @@ -383,6 +383,8 @@ impl TypeDef<'_> { InnerTypeKind::Cont(ct) => Cont(ct.into()), }, shared: self.shared, + descriptor: self.descriptor.map(|i| i.unwrap_u32()), + describes: self.describes.map(|i| i.unwrap_u32()), }; wasm_encoder::SubType { composite_type, @@ -438,6 +440,7 @@ impl From> for wasm_encoder::HeapType { Self::Abstract { shared, ty } } HeapType::Concrete(i) => Self::Concrete(i.unwrap_u32()), + HeapType::Exact(i) => Self::Exact(i.unwrap_u32()), } } } @@ -1520,3 +1523,43 @@ impl Encode for BrOnCastFail<'_> { self.to_type.heap.encode(e); } } + +impl Encode for RefCastDesc<'_> { + fn encode(&self, e: &mut Vec) { + e.push(0xfb); + if self.r#type.nullable { + e.push(0x24); + } else { + e.push(0x23); + } + self.r#type.heap.encode(e); + } +} + +impl Encode for BrOnCastDesc<'_> { + fn encode(&self, e: &mut Vec) { + e.push(0xfb); + e.push(0x25); + e.push(br_on_cast_flags( + self.from_type.nullable, + self.to_type.nullable, + )); + self.label.encode(e); + self.from_type.heap.encode(e); + self.to_type.heap.encode(e); + } +} + +impl Encode for BrOnCastDescFail<'_> { + fn encode(&self, e: &mut Vec) { + e.push(0xfb); + e.push(0x26); + e.push(br_on_cast_flags( + self.from_type.nullable, + self.to_type.nullable, + )); + self.label.encode(e); + self.from_type.heap.encode(e); + self.to_type.heap.encode(e); + } +} diff --git a/crates/wast/src/core/expr.rs b/crates/wast/src/core/expr.rs index 20927b4c8f..723d1de6b8 100644 --- a/crates/wast/src/core/expr.rs +++ b/crates/wast/src/core/expr.rs @@ -1202,6 +1202,12 @@ instructions! { I64Sub128 : [0xfc, 20] : "i64.sub128", I64MulWideS : [0xfc, 21] : "i64.mul_wide_s", I64MulWideU : [0xfc, 22] : "i64.mul_wide_u", + + // Custom descriptors + RefGetDesc(Index<'a>): [0xfb, 34] : "ref.get_desc", + RefCastDesc(RefCastDesc<'a>) : [] : "ref.cast_desc", + BrOnCastDesc(Box>) : [] : "br_on_cast_desc", + BrOnCastDescFail(Box>) : [] : "br_on_cast_desc_fail", } } @@ -1946,6 +1952,63 @@ impl<'a> Parse<'a> for BrOnCastFail<'a> { } } +/// Extra data associated with the `ref.cast_desc` instruction +#[derive(Debug, Clone)] +pub struct RefCastDesc<'a> { + /// The type to cast to. + pub r#type: RefType<'a>, +} + +impl<'a> Parse<'a> for RefCastDesc<'a> { + fn parse(parser: Parser<'a>) -> Result { + Ok(RefCastDesc { + r#type: parser.parse()?, + }) + } +} + +/// Extra data associated with the `br_on_cast_desc` instruction +#[derive(Debug, Clone)] +pub struct BrOnCastDesc<'a> { + /// The label to branch to. + pub label: Index<'a>, + /// The type we're casting from. + pub from_type: RefType<'a>, + /// The type we're casting to. + pub to_type: RefType<'a>, +} + +impl<'a> Parse<'a> for BrOnCastDesc<'a> { + fn parse(parser: Parser<'a>) -> Result { + Ok(BrOnCastDesc { + label: parser.parse()?, + from_type: parser.parse()?, + to_type: parser.parse()?, + }) + } +} + +/// Extra data associated with the `br_on_cast_desc_fail` instruction +#[derive(Debug, Clone)] +pub struct BrOnCastDescFail<'a> { + /// The label to branch to. + pub label: Index<'a>, + /// The type we're casting from. + pub from_type: RefType<'a>, + /// The type we're casting to. + pub to_type: RefType<'a>, +} + +impl<'a> Parse<'a> for BrOnCastDescFail<'a> { + fn parse(parser: Parser<'a>) -> Result { + Ok(BrOnCastDescFail { + label: parser.parse()?, + from_type: parser.parse()?, + to_type: parser.parse()?, + }) + } +} + /// The memory ordering for atomic instructions. /// /// For an in-depth explanation of memory orderings, see the C++ documentation diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs index 533e43f050..eff77e75db 100644 --- a/crates/wast/src/core/resolve/names.rs +++ b/crates/wast/src/core/resolve/names.rs @@ -673,6 +673,23 @@ impl<'a, 'b> ExprResolver<'a, 'b> { self.resolver.resolve(&mut s.tag_index, Ns::Tag)?; } + RefGetDesc(i) => { + self.resolver.resolve(i, Ns::Type)?; + } + RefCastDesc(i) => { + self.resolver.resolve_reftype(&mut i.r#type)?; + } + BrOnCastDesc(i) => { + self.resolve_label(&mut i.label)?; + self.resolver.resolve_reftype(&mut i.to_type)?; + self.resolver.resolve_reftype(&mut i.from_type)?; + } + BrOnCastDescFail(i) => { + self.resolve_label(&mut i.label)?; + self.resolver.resolve_reftype(&mut i.to_type)?; + self.resolver.resolve_reftype(&mut i.from_type)?; + } + _ => {} } Ok(()) @@ -813,6 +830,12 @@ pub(crate) trait ResolveCoreType<'a> { if let Some(parent) = &mut ty.parent { self.resolve_type_name(parent)?; } + if let Some(descriptor) = &mut ty.descriptor { + self.resolve_type_name(descriptor)?; + } + if let Some(describes) = &mut ty.describes { + self.resolve_type_name(describes)?; + } match &mut ty.kind { InnerTypeKind::Func(func) => self.resolve_type_func(func), InnerTypeKind::Struct(struct_) => { @@ -853,7 +876,7 @@ pub(crate) trait ResolveCoreType<'a> { fn resolve_heaptype(&mut self, ty: &mut HeapType<'a>) -> Result<(), Error> { match ty { - HeapType::Concrete(i) => { + HeapType::Concrete(i) | HeapType::Exact(i) => { self.resolve_type_name(i)?; } HeapType::Abstract { .. } => {} diff --git a/crates/wast/src/core/resolve/types.rs b/crates/wast/src/core/resolve/types.rs index 7a7e36c9d2..3030c1c21a 100644 --- a/crates/wast/src/core/resolve/types.rs +++ b/crates/wast/src/core/resolve/types.rs @@ -264,6 +264,8 @@ impl<'a> TypeKey<'a> for FuncKey<'a> { }), shared, parent: None, + descriptor: None, + describes: None, final_type: None, } } diff --git a/crates/wast/src/core/types.rs b/crates/wast/src/core/types.rs index d3521c8c59..ede2d58340 100644 --- a/crates/wast/src/core/types.rs +++ b/crates/wast/src/core/types.rs @@ -68,6 +68,9 @@ pub enum HeapType<'a> { /// A reference to a concrete function, struct, or array type defined by /// Wasm: `ref T`. This is part of the function references and GC proposals. Concrete(Index<'a>), + /// A reference to an exact type. + /// This is part of the custom descriptors proposal. + Exact(Index<'a>), } impl<'a> Parse<'a> for HeapType<'a> { @@ -77,11 +80,16 @@ impl<'a> Parse<'a> for HeapType<'a> { Ok(HeapType::Concrete(parser.parse()?)) } else if l.peek::()? { parser.parens(|p| { - p.parse::()?; - Ok(HeapType::Abstract { - shared: true, - ty: p.parse()?, - }) + if l.peek::()? { + p.parse::()?; + Ok(HeapType::Exact(p.parse()?)) + } else { + p.parse::()?; + Ok(HeapType::Abstract { + shared: true, + ty: p.parse()?, + }) + } }) } else if l.peek::()? { Ok(HeapType::Abstract { @@ -957,49 +965,84 @@ pub struct TypeDef<'a> { pub shared: bool, /// The declared parent type of this definition. pub parent: Option>, + /// The descriptor type. + pub descriptor: Option>, + /// The descriptor for type. + pub describes: Option>, /// Whether this type is final or not. By default types are final. pub final_type: Option, } +fn parse_optional<'a, K: Peek + Parse<'a>, R, T>( + parser: Parser<'a>, + parse: impl FnOnce(Parser<'a>) -> Result, + default: R, + f: impl FnOnce(Parser<'a>, R) -> Result, +) -> Result { + if parser.peek::()? { + parser.parse::()?; + let result: R = parse(parser)?; + parser.parens(|parser: Parser| f(parser, result)) + } else { + f(parser, default) + } +} + impl<'a> Parse<'a> for TypeDef<'a> { fn parse(parser: Parser<'a>) -> Result { let parse_shared_and_kind = |parser: Parser<'a>| { - if parser.peek::()? { - parser.parse::()?; - parser.parens(|parser| { - let kind = parser.parse()?; - Ok((true, kind)) - }) - } else { - let kind = parser.parse()?; - Ok((false, kind)) - } + parse_optional::( + parser, + |_| Ok(true), + false, + |parser, shared| { + parse_optional::( + parser, + |p| Ok(Some(p.parse::()?)), + None, + |parser, describes| { + parse_optional::( + parser, + |p| Ok(Some(p.parse::()?)), + None, + |parser, descriptor| { + let kind = parser.parse()?; + Ok((shared, descriptor, describes, kind)) + }, + ) + }, + ) + }, + ) }; - let (parent, (shared, kind), final_type) = if parser.peek::()? { - parser.parse::()?; + let (parent, (shared, descriptor, describes, kind), final_type) = + if parser.peek::()? { + parser.parse::()?; - let final_type: Option = if parser.peek::()? { - parser.parse::()?; - Some(true) - } else { - Some(false) - }; + let final_type: Option = if parser.peek::()? { + parser.parse::()?; + Some(true) + } else { + Some(false) + }; - let parent = if parser.peek::>()? { - parser.parse()? + let parent = if parser.peek::>()? { + parser.parse()? + } else { + None + }; + let pair = parser.parens(parse_shared_and_kind)?; + (parent, pair, final_type) } else { - None + (None, parse_shared_and_kind(parser)?, None) }; - let pair = parser.parens(parse_shared_and_kind)?; - (parent, pair, final_type) - } else { - (None, parse_shared_and_kind(parser)?, None) - }; Ok(TypeDef { kind, shared, parent, + descriptor, + describes, final_type, }) } diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs index d1fdbe6380..fa24096faf 100644 --- a/crates/wast/src/lib.rs +++ b/crates/wast/src/lib.rs @@ -416,10 +416,13 @@ pub mod kw { custom_keyword!(data); custom_keyword!(declare); custom_keyword!(delegate); + custom_keyword!(descriptor); + custom_keyword!(describes); custom_keyword!(r#do = "do"); custom_keyword!(dtor); custom_keyword!(elem); custom_keyword!(end); + custom_keyword!(exact); custom_keyword!(tag); custom_keyword!(exn); custom_keyword!(exnref); diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index 71a58e4d26..bf9d777267 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -454,7 +454,7 @@ impl<'a> Module<'a> { fn heapty(&mut self, ty: HeapType) { match ty { HeapType::Abstract { .. } => {} - HeapType::Concrete(i) => self.ty(i.as_module_index().unwrap()), + HeapType::Concrete(i) | HeapType::Exact(i) => self.ty(i.as_module_index().unwrap()), } } diff --git a/tests/cli/custom-descriptors-ops.wast b/tests/cli/custom-descriptors-ops.wast new file mode 100644 index 0000000000..24d25eec11 --- /dev/null +++ b/tests/cli/custom-descriptors-ops.wast @@ -0,0 +1,191 @@ +;; RUN: wast --assert default --snapshot tests/snapshots % -f custom-descriptors + +;; --enable-gc + +(module + (type $f (func (result i32))) + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + ) + + (import "" "rtt1" (global $g1 (ref (exact $t2)))) + (import "" "rtt1sub" (global $g1sub (ref (exact $t2sub)))) + + (elem declare func $f_impl) + (func $f_impl (type $f) (result i32) + unreachable + ) + + (func $test-struct-new + i32.const 3 + global.get $g1 + struct.new $t1 + drop + + global.get $g1 + struct.new_default $t1 + drop + + global.get $g1sub + struct.new_default $t1sub + drop + ) + + (func $test-exact-types + (local $l_any anyref) + (local $l_struct structref) + (local $l_t2 (ref $t2)) + (local $l_t2_e (ref (exact $t2))) + global.get $g1 + local.set $l_any + global.get $g1 + local.set $l_struct + global.get $g1 + local.set $l_t2 + global.get $g1 + local.set $l_t2_e + + global.get $g1sub + local.set $l_struct + global.get $g1sub + local.set $l_t2 + + local.get $l_any + ref.cast (ref null $t2) + ref.cast (ref (exact $t2)) + local.set $l_t2_e + ) + + (func $test-struct-new-result + (local (ref (exact $t2))) + ref.func $f_impl + struct.new $t2 + local.set 0 + ) + + (func $test-get-desc (param $rtt (ref (exact $t2))) + local.get $rtt + struct.new_default $t1 + ref.get_desc $t1 + local.set $rtt + ) + + (func $test-get-desc-2 (result (ref (exact $t2))) + unreachable + ref.get_desc $t1 + ) + + (func $test-cast-desc (param $i (ref $t1)) (param $desc (ref null (exact $t2))) (result (ref null (exact $t1))) + local.get $i + local.get $desc + ref.cast_desc (ref null (exact $t1)) + ) + + (func $test-br_on_cast_desc (param $i (ref null $t1sub)) (param $desc (ref null $t2)) (result (ref null $t1)) + local.get $i + local.get $desc + br_on_cast_desc 0 (ref null $t1sub) (ref null $t1) + ) + + (func $test-br_on_cast_desc_fail (param $i (ref null $t1sub)) (param $desc (ref null $t2)) (result (ref null $t1)) + local.get $i + local.get $desc + br_on_cast_desc_fail 0 (ref null $t1sub) (ref null $t1) + ) +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + + (import "" "rtt1" (global $g1 (ref (exact $t2)))) + (import "" "rtt1sub" (global $g1sub (ref (exact $t2sub)))) + + (func $test-struct-new-non-exact-1 + global.get $g1sub + i32.const 3 + struct.new $t1 + drop + ) + ) + "type mismatch: expected (ref null (exact" +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + + (import "" "rtt1" (global $g1 (ref (exact $t2)))) + (import "" "rtt1sub" (global $g1sub (ref (exact $t2sub)))) + + (func $test-struct-new-non-exact-2 (param (ref $t2)) + local.get 0 + struct.new_default $t1 + drop + ) + ) + "type mismatch: expected (ref null (exact" +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + + (import "" "rtt1" (global $g1 (ref (exact $t2)))) + (import "" "rtt1sub" (global $g1sub (ref (exact $t2sub)))) + + (func $test-struct-new-non-exact-3 + global.get $g1 + i32.const 2 + f32.const 1 + struct.new $t1sub + drop + ) + ) + "type mismatch: expected (ref null (exact" +) + + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + + (import "" "rtt1" (global $g1 (ref (exact $t2)))) + (import "" "rtt1sub" (global $g1sub (ref (exact $t2sub)))) + + (func $test-br_on_cast_desc-bad-types (param $i (ref null $f)) (param $desc (ref null $t2)) (result (ref null $t1)) + local.get $i + local.get $desc + br_on_cast_desc 0 (ref null $f) (ref null $t1) + ) + + ) + "type mismatch:" +) diff --git a/tests/cli/custom-descriptors.wast b/tests/cli/custom-descriptors.wast new file mode 100644 index 0000000000..eda497abf7 --- /dev/null +++ b/tests/cli/custom-descriptors.wast @@ -0,0 +1,166 @@ +;; RUN: wast --assert default --snapshot tests/snapshots % -f custom-descriptors + +;; --enable-gc + +(module + (rec + (type $t1 (descriptor $t2 (struct (field i32)))) + (type $t2 (describes $t1 (struct (field (ref $f))))) + (type $f (func (result i32))) + ) +) + +(assert_invalid + (module + (rec + (type $f (func (result i32))) + (type $t2 (describes $t1 (struct (field (ref $f))))) + (type $t1 (descriptor $t2 (struct (field i32)))) + ) + ) + "forward describes reference" +) + +(assert_invalid + (module + (rec + (type $t1 (descriptor $t2 (array f32))) + (type $t2 (describes $t1 (struct (field funcref)))) + ) + ) + "descriptor clause on non-struct type" +) + +(assert_invalid + (module + (rec + (type $t1 (descriptor $t2 (struct (field i32)))) + (type $t2 (describes $t1 (func))) + ) + ) + "describes clause on non-struct type" +) + +(module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + ) + "supertype of described type must be described by supertype of descriptor" +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1n (sub (descriptor $t2n (struct (field i64))))) + (type $t2n (sub (describes $t1n (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2n (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + ) + "supertype of described type must be described by supertype of descriptor" +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (struct (field i32 f32)))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $f (func (result i32))) + ) + ) + "supertype of type without descriptor cannot have descriptor" +) + +(assert_invalid + (module + (rec + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (sub $t1 (descriptor $t2sub (struct (field i32 f32))))) + (type $t2sub (sub $t2 (describes $t1sub (struct (field (ref $f) (ref $f)))))) + (type $t2sub-n (sub $t2sub (struct (field (ref $f) (ref $f) (ref $f))))) + (type $f (func (result i32))) + ) + ) + "supertype of non-descriptor type cannot be a descriptor" +) + +(assert_invalid + (module + (rec + (type $t0 (sub (struct (field (ref $f))))) + (type $t1 (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (sub $t0 (describes $t1 (struct (field (ref $f)))))) + (type $f (func (result i32))) + ) + ) + "supertype of descriptor must be a descriptor" +) + +(assert_invalid + (module + (rec + (type $f (func (result i32))) + (type $t1 (descriptor $t2 (struct (field i32)))) + (type $t2 (struct (field (ref $f)))) + ) + ) + "descriptor with no matching describes" +) + +(assert_invalid + (module + (rec + (type $f (func (result i32))) + (type $t1 (descriptor $t2 (struct (field i32)))) + (type $t1b (descriptor $t2 (struct (field i32)))) + (type $t2 (describes $t1b (struct (field (ref $f))))) + ) + ) + "descriptor with no matching describes" +) + + +(assert_invalid + (module + (rec + (type $f (func (result i32))) + (type $t1 (struct (field i32))) + (type $t2 (describes $t1 (struct (field (ref $f))))) + ) + ) + "describes with no matching descriptor" +) + +(assert_invalid + (module + (rec + (type $f (func (result i32))) + (type $t1 (descriptor $t2 (struct (field i32)))) + (type $t2 (describes $t1 (struct (field (ref $f))))) + (type $t2b (describes $t1 (struct (field (ref $f))))) + ) + ) + "describes with no matching descriptor" +) diff --git a/tests/cli/dump-branch-hints.wat.stdout b/tests/cli/dump-branch-hints.wat.stdout index 9836b36909..dd5e5e701e 100644 --- a/tests/cli/dump-branch-hints.wat.stdout +++ b/tests/cli/dump-branch-hints.wat.stdout @@ -3,7 +3,7 @@ 0x8 | 01 04 | type section 0xa | 01 | 1 count --- rec group 0 (implicit) --- - 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0xe | 03 02 | func section 0x10 | 01 | 1 count 0x11 | 00 | [func 0] type 0 diff --git a/tests/cli/dump-elem-segments.wat.stdout b/tests/cli/dump-elem-segments.wat.stdout index 7228f869bf..f21f86184f 100644 --- a/tests/cli/dump-elem-segments.wat.stdout +++ b/tests/cli/dump-elem-segments.wat.stdout @@ -3,7 +3,7 @@ 0x8 | 01 04 | type section 0xa | 01 | 1 count --- rec group 0 (implicit) --- - 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0xe | 03 02 | func section 0x10 | 01 | 1 count 0x11 | 00 | [func 0] type 0 diff --git a/tests/cli/dump-llvm-object.wat.stdout b/tests/cli/dump-llvm-object.wat.stdout index fb518e2210..cdcb83450d 100644 --- a/tests/cli/dump-llvm-object.wat.stdout +++ b/tests/cli/dump-llvm-object.wat.stdout @@ -3,21 +3,21 @@ 0x8 | 01 22 | type section 0xa | 06 | 6 count --- rec group 0 (implicit) --- - 0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } } + 0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } --- rec group 1 (implicit) --- - 0xf | 60 04 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }), shared: false } } + 0xf | 60 04 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 7f 01 7f --- rec group 2 (implicit) --- - 0x17 | 60 05 7f 7f | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32, I32], results: [I32] }), shared: false } } + 0x17 | 60 05 7f 7f | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32, I32], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 7f 7f 01 | 7f --- rec group 3 (implicit) --- - 0x20 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32] }), shared: false } } + 0x20 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f --- rec group 4 (implicit) --- - 0x25 | 60 00 01 7f | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [I32] }), shared: false } } + 0x25 | 60 00 01 7f | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } --- rec group 5 (implicit) --- - 0x29 | 60 00 00 | [type 5] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x29 | 60 00 00 | [type 5] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0x2c | 02 8b 01 | import section 0x2f | 04 | 4 count 0x30 | 03 65 6e 76 | import [memory 0] Import { module: "env", name: "__linear_memory", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) } diff --git a/tests/cli/dump/alias.wat.stdout b/tests/cli/dump/alias.wat.stdout index 7100013041..a4504f0c02 100644 --- a/tests/cli/dump/alias.wat.stdout +++ b/tests/cli/dump/alias.wat.stdout @@ -31,7 +31,7 @@ 0x57 | 01 04 | type section 0x59 | 01 | 1 count --- rec group 0 (implicit) --- - 0x5a | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x5a | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 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 47695fca9e..d1e3991bd3 100644 --- a/tests/cli/dump/alias2.wat.stdout +++ b/tests/cli/dump/alias2.wat.stdout @@ -127,7 +127,7 @@ 0x158 | 01 04 | type section 0x15a | 01 | 1 count --- rec group 0 (implicit) --- - 0x15b | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x15b | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0x15e | 03 02 | func section 0x160 | 01 | 1 count 0x161 | 00 | [func 0] type 0 @@ -164,7 +164,7 @@ 0x1a2 | 01 04 | type section 0x1a4 | 01 | 1 count --- rec group 0 (implicit) --- - 0x1a5 | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x1a5 | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 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 a923deea11..e005a15559 100644 --- a/tests/cli/dump/blockty.wat.stdout +++ b/tests/cli/dump/blockty.wat.stdout @@ -3,16 +3,16 @@ 0x8 | 01 17 | type section 0xa | 05 | 5 count --- rec group 0 (implicit) --- - 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } --- rec group 1 (implicit) --- - 0xe | 60 00 01 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [I32] }), shared: false } } + 0xe | 60 00 01 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } --- rec group 2 (implicit) --- - 0x12 | 60 01 7f 00 | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } } + 0x12 | 60 01 7f 00 | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } --- rec group 3 (implicit) --- - 0x16 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32] }), shared: false } } + 0x16 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f --- rec group 4 (implicit) --- - 0x1b | 60 01 7f 02 | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32, I32] }), shared: false } } + 0x1b | 60 01 7f 02 | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32, I32] }), shared: false, descriptor_idx: None, describes_idx: None } } | 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 ff40ee87fe..49c7404bbd 100644 --- a/tests/cli/dump/bundled.wat.stdout +++ b/tests/cli/dump/bundled.wat.stdout @@ -26,7 +26,7 @@ 0x54 | 01 09 | type section 0x56 | 01 | 1 count --- rec group 0 (implicit) --- - 0x57 | 60 04 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }), shared: false } } + 0x57 | 60 04 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 7f 01 7f 0x5f | 03 02 | func section 0x61 | 01 | 1 count @@ -63,10 +63,10 @@ 0xa0 | 01 09 | type section 0xa2 | 02 | 2 count --- rec group 0 (implicit) --- - 0xa3 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [] }), shared: false } } + 0xa3 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } | 00 --- rec group 1 (implicit) --- - 0xa8 | 60 00 00 | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0xa8 | 60 00 00 | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 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) } @@ -107,10 +107,10 @@ 0x101 | 01 0c | type section 0x103 | 02 | 2 count --- rec group 0 (implicit) --- - 0x104 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [] }), shared: false } } + 0x104 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } | 00 --- rec group 1 (implicit) --- - 0x109 | 60 03 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32], results: [] }), shared: false } } + 0x109 | 60 03 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } | 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 d46b131707..4c5619538a 100644 --- a/tests/cli/dump/component-expand-bundle.wat.stdout +++ b/tests/cli/dump/component-expand-bundle.wat.stdout @@ -6,7 +6,7 @@ 0x12 | 01 04 | type section 0x14 | 01 | 1 count --- rec group 0 (implicit) --- - 0x15 | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x15 | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0x18 | 03 02 | func section 0x1a | 01 | 1 count 0x1b | 00 | [func 0] type 0 @@ -30,7 +30,7 @@ 0x3d | 01 04 | type section 0x3f | 01 | 1 count --- rec group 0 (implicit) --- - 0x40 | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x40 | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 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 6ce5dff9f5..2b50be09ce 100644 --- a/tests/cli/dump/import-modules.wat.stdout +++ b/tests/cli/dump/import-modules.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 03 0d | core type section 0xa | 01 | 1 count - 0xb | 50 02 01 60 | [core type 0] Module([Type(RecGroup { inner: Implicit((14, SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } })) }), Import(Import { module: "", name: "f", ty: Func(0) })]) + 0xb | 50 02 01 60 | [core type 0] Module([Type(RecGroup { inner: Implicit((14, SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } })) }), Import(Import { module: "", name: "f", ty: Func(0) })]) | 00 00 00 00 | 01 66 00 00 0x17 | 0a 07 | component import section @@ -15,7 +15,7 @@ 0x2a | 01 04 | type section 0x2c | 01 | 1 count --- rec group 0 (implicit) --- - 0x2d | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0x2d | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0x30 | 03 02 | func section 0x32 | 01 | 1 count 0x33 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/module-types.wat.stdout b/tests/cli/dump/module-types.wat.stdout index 8a8c2d5b24..52e1b1badf 100644 --- a/tests/cli/dump/module-types.wat.stdout +++ b/tests/cli/dump/module-types.wat.stdout @@ -2,7 +2,7 @@ | 0d 00 01 00 0x8 | 03 23 | core type section 0xa | 01 | 1 count - 0xb | 50 05 01 60 | [core type 0] Module([Type(RecGroup { inner: Implicit((14, SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } })) }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false, shared: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, table64: false, initial: 1, maximum: None, shared: false }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) })]) + 0xb | 50 05 01 60 | [core type 0] Module([Type(RecGroup { inner: Implicit((14, SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } })) }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false, shared: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, table64: false, initial: 1, maximum: None, shared: false }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) })]) | 00 00 00 00 | 01 66 00 00 | 00 00 01 67 diff --git a/tests/cli/dump/names.wat.stdout b/tests/cli/dump/names.wat.stdout index 41ee06cc51..7c5e5de259 100644 --- a/tests/cli/dump/names.wat.stdout +++ b/tests/cli/dump/names.wat.stdout @@ -3,7 +3,7 @@ 0x8 | 01 05 | type section 0xa | 01 | 1 count --- rec group 0 (implicit) --- - 0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } } + 0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 0xf | 03 02 | func section 0x11 | 01 | 1 count 0x12 | 00 | [func 0] type 0 diff --git a/tests/cli/dump/rec-group.wat.stdout b/tests/cli/dump/rec-group.wat.stdout index 07317e38a4..20d5142571 100644 --- a/tests/cli/dump/rec-group.wat.stdout +++ b/tests/cli/dump/rec-group.wat.stdout @@ -4,25 +4,25 @@ 0xa | 04 | 4 count --- rec group 0 (explicit) --- 0xb | 4e 01 | - 0xd | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [F64] }), shared: false } } + 0xd | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [F64] }), shared: false, descriptor_idx: None, describes_idx: None } } | 01 7c --- rec group 1 (explicit) --- 0x13 | 4e 03 | - 0x15 | 50 00 5f 01 | [type 1] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: false }] }), shared: false } } + 0x15 | 50 00 5f 01 | [type 1] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: false }] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 00 - 0x1b | 50 00 5f 01 | [type 2] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }] }), shared: false } } + 0x1b | 50 00 5f 01 | [type 2] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 01 - 0x21 | 50 00 5f 08 | [type 3] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }, FieldType { element_type: Val(I64), mutable: true }, FieldType { element_type: Val(F32), mutable: true }, FieldType { element_type: Val(F64), mutable: true }, FieldType { element_type: Val(V128), mutable: true }, FieldType { element_type: Val(Ref(funcref)), mutable: true }, FieldType { element_type: Val(Ref(externref)), mutable: true }, FieldType { element_type: Val(Ref((ref null (module 2)))), mutable: true }] }), shared: false } } + 0x21 | 50 00 5f 08 | [type 3] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }, FieldType { element_type: Val(I64), mutable: true }, FieldType { element_type: Val(F32), mutable: true }, FieldType { element_type: Val(F64), mutable: true }, FieldType { element_type: Val(V128), mutable: true }, FieldType { element_type: Val(Ref(funcref)), mutable: true }, FieldType { element_type: Val(Ref(externref)), mutable: true }, FieldType { element_type: Val(Ref((ref null (module 2)))), mutable: true }] }), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 01 7e 01 | 7d 01 7c 01 | 7b 01 70 01 | 6f 01 63 02 | 01 --- rec group 2 (implicit) --- - 0x36 | 50 00 5e 7f | [type 4] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })), shared: false } } + 0x36 | 50 00 5e 7f | [type 4] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })), shared: false, descriptor_idx: None, describes_idx: None } } | 00 --- rec group 3 (implicit) --- - 0x3b | 50 01 04 5e | [type 5] SubType { is_final: false, supertype_idx: Some(CoreTypeIndex { kind: "module", index: 4 }), composite_type: CompositeType { inner: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })), shared: false } } + 0x3b | 50 01 04 5e | [type 5] SubType { is_final: false, supertype_idx: Some(CoreTypeIndex { kind: "module", index: 4 }), composite_type: CompositeType { inner: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })), shared: false, descriptor_idx: None, describes_idx: None } } | 7f 00 0x41 | 00 0e | custom section 0x43 | 04 6e 61 6d | name: "name" diff --git a/tests/cli/dump/select.wat.stdout b/tests/cli/dump/select.wat.stdout index 98937ed108..0559788422 100644 --- a/tests/cli/dump/select.wat.stdout +++ b/tests/cli/dump/select.wat.stdout @@ -3,7 +3,7 @@ 0x8 | 01 04 | type section 0xa | 01 | 1 count --- rec group 0 (implicit) --- - 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0xb | 60 00 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 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 b01a25e80b..a5fbf05922 100644 --- a/tests/cli/dump/simple.wat.stdout +++ b/tests/cli/dump/simple.wat.stdout @@ -3,9 +3,9 @@ 0x8 | 01 08 | type section 0xa | 02 | 2 count --- rec group 0 (implicit) --- - 0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } } + 0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } --- rec group 1 (implicit) --- - 0xf | 60 00 00 | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } } + 0xf | 60 00 00 | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false, descriptor_idx: None, describes_idx: None } } 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/print-operand-stack-folded.wat.stdout b/tests/cli/print-operand-stack-folded.wat.stdout index 13de435f13..3d59eb915e 100644 --- a/tests/cli/print-operand-stack-folded.wat.stdout +++ b/tests/cli/print-operand-stack-folded.wat.stdout @@ -23,7 +23,7 @@ (i32.const 7 (; [i32 i32] ;)))) (table.set 0 (; [] ;) (i32.const 2 (; [i32] ;)) - (struct.new_default $st (; [i32 (ref (id 1))] ;))) + (struct.new_default $st (; [i32 (ref (exact (id 1)))] ;))) (table.set 0 (; [] ;) (i32.const 3 (; [i32] ;)) (array.new_default $at (; [i32 (ref (id 2))] ;) diff --git a/tests/cli/print-operand-stack.wat.stdout b/tests/cli/print-operand-stack.wat.stdout index e1e61b2865..a2b456c2f6 100644 --- a/tests/cli/print-operand-stack.wat.stdout +++ b/tests/cli/print-operand-stack.wat.stdout @@ -31,7 +31,7 @@ i32.const 2 ;; [i32] struct.new_default $st - ;; [i32 (ref (id 1))] + ;; [i32 (ref (exact (id 1)))] table.set 0 ;; [] i32.const 3 diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr index 8b93bad803..7097b34673 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, cm-fixed-size-list, cm-gc, call-indirect-overlong, bulk-memory-opt, mvp, wasm1, wasm2, wasm3, lime1, 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, cm-gc, call-indirect-overlong, bulk-memory-opt, custom-descriptors, mvp, wasm1, wasm2, wasm3, lime1, all For more information, try '--help'. diff --git a/tests/snapshots/cli/custom-descriptors-ops.wast.json b/tests/snapshots/cli/custom-descriptors-ops.wast.json new file mode 100644 index 0000000000..1a4ebce47e --- /dev/null +++ b/tests/snapshots/cli/custom-descriptors-ops.wast.json @@ -0,0 +1,39 @@ +{ + "source_filename": "tests/cli/custom-descriptors-ops.wast", + "commands": [ + { + "type": "module", + "line": 5, + "filename": "custom-descriptors-ops.0.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 101, + "filename": "custom-descriptors-ops.1.wasm", + "module_type": "binary", + "text": "type mismatch: expected (ref null (exact" + }, + { + "type": "assert_invalid", + "line": 124, + "filename": "custom-descriptors-ops.2.wasm", + "module_type": "binary", + "text": "type mismatch: expected (ref null (exact" + }, + { + "type": "assert_invalid", + "line": 146, + "filename": "custom-descriptors-ops.3.wasm", + "module_type": "binary", + "text": "type mismatch: expected (ref null (exact" + }, + { + "type": "assert_invalid", + "line": 171, + "filename": "custom-descriptors-ops.4.wasm", + "module_type": "binary", + "text": "type mismatch:" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/cli/custom-descriptors-ops.wast/0.print b/tests/snapshots/cli/custom-descriptors-ops.wast/0.print new file mode 100644 index 0000000000..8080f705d5 --- /dev/null +++ b/tests/snapshots/cli/custom-descriptors-ops.wast/0.print @@ -0,0 +1,82 @@ +(module + (type $f (;0;) (func (result i32))) + (rec + (type $t1 (;1;) (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (;2;) (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (;3;) (sub $t1 (descriptor $t2sub (struct (field i32) (field f32))))) + (type $t2sub (;4;) (sub $t2 (describes $t1sub (struct (field (ref $f)) (field (ref $f)))))) + ) + (type (;5;) (func)) + (type (;6;) (func (param (ref (exact $t2))))) + (type (;7;) (func (result (ref (exact $t2))))) + (type (;8;) (func (param (ref $t1) (ref null (exact $t2))) (result (ref null (exact $t1))))) + (type (;9;) (func (param (ref null $t1sub) (ref null $t2)) (result (ref null $t1)))) + (import "" "rtt1" (global $g1 (;0;) (ref (exact $t2)))) + (import "" "rtt1sub" (global $g1sub (;1;) (ref (exact $t2sub)))) + (elem (;0;) declare func $f_impl) + (func $f_impl (;0;) (type $f) (result i32) + unreachable + ) + (func $test-struct-new (;1;) (type 5) + i32.const 3 + global.get $g1 + struct.new $t1 + drop + global.get $g1 + struct.new_default $t1 + drop + global.get $g1sub + struct.new_default $t1sub + drop + ) + (func $test-exact-types (;2;) (type 5) + (local $l_any anyref) (local $l_struct structref) (local $l_t2 (ref $t2)) (local $l_t2_e (ref (exact $t2))) + global.get $g1 + local.set $l_any + global.get $g1 + local.set $l_struct + global.get $g1 + local.set $l_t2 + global.get $g1 + local.set $l_t2_e + global.get $g1sub + local.set $l_struct + global.get $g1sub + local.set $l_t2 + local.get $l_any + ref.cast (ref null $t2) + ref.cast (ref (exact $t2)) + local.set $l_t2_e + ) + (func $test-struct-new-result (;3;) (type 5) + (local (ref (exact $t2))) + ref.func $f_impl + struct.new $t2 + local.set 0 + ) + (func $test-get-desc (;4;) (type 6) (param $rtt (ref (exact $t2))) + local.get $rtt + struct.new_default $t1 + ref.get_desc $t1 + local.set $rtt + ) + (func $test-get-desc-2 (;5;) (type 7) (result (ref (exact $t2))) + unreachable + ref.get_desc $t1 + ) + (func $test-cast-desc (;6;) (type 8) (param $i (ref $t1)) (param $desc (ref null (exact $t2))) (result (ref null (exact $t1))) + local.get $i + local.get $desc + ref.cast_desc (ref null (exact $t1)) + ) + (func $test-br_on_cast_desc (;7;) (type 9) (param $i (ref null $t1sub)) (param $desc (ref null $t2)) (result (ref null $t1)) + local.get $i + local.get $desc + br_on_cast_desc 0 (ref null $t1sub) (ref null $t1) + ) + (func $test-br_on_cast_desc_fail (;8;) (type 9) (param $i (ref null $t1sub)) (param $desc (ref null $t2)) (result (ref null $t1)) + local.get $i + local.get $desc + br_on_cast_desc_fail 0 (ref null $t1sub) (ref null $t1) + ) +) diff --git a/tests/snapshots/cli/custom-descriptors.wast.json b/tests/snapshots/cli/custom-descriptors.wast.json new file mode 100644 index 0000000000..b75c7b711e --- /dev/null +++ b/tests/snapshots/cli/custom-descriptors.wast.json @@ -0,0 +1,101 @@ +{ + "source_filename": "tests/cli/custom-descriptors.wast", + "commands": [ + { + "type": "module", + "line": 5, + "filename": "custom-descriptors.0.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 14, + "filename": "custom-descriptors.1.wasm", + "module_type": "binary", + "text": "forward describes reference" + }, + { + "type": "assert_invalid", + "line": 25, + "filename": "custom-descriptors.2.wasm", + "module_type": "binary", + "text": "descriptor clause on non-struct type" + }, + { + "type": "assert_invalid", + "line": 35, + "filename": "custom-descriptors.3.wasm", + "module_type": "binary", + "text": "describes clause on non-struct type" + }, + { + "type": "module", + "line": 44, + "filename": "custom-descriptors.4.wasm", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 55, + "filename": "custom-descriptors.5.wasm", + "module_type": "binary", + "text": "supertype of described type must be described by supertype of descriptor" + }, + { + "type": "assert_invalid", + "line": 68, + "filename": "custom-descriptors.6.wasm", + "module_type": "binary", + "text": "supertype of described type must be described by supertype of descriptor" + }, + { + "type": "assert_invalid", + "line": 83, + "filename": "custom-descriptors.7.wasm", + "module_type": "binary", + "text": "supertype of type without descriptor cannot have descriptor" + }, + { + "type": "assert_invalid", + "line": 96, + "filename": "custom-descriptors.8.wasm", + "module_type": "binary", + "text": "supertype of non-descriptor type cannot be a descriptor" + }, + { + "type": "assert_invalid", + "line": 110, + "filename": "custom-descriptors.9.wasm", + "module_type": "binary", + "text": "supertype of descriptor must be a descriptor" + }, + { + "type": "assert_invalid", + "line": 122, + "filename": "custom-descriptors.10.wasm", + "module_type": "binary", + "text": "descriptor with no matching describes" + }, + { + "type": "assert_invalid", + "line": 133, + "filename": "custom-descriptors.11.wasm", + "module_type": "binary", + "text": "descriptor with no matching describes" + }, + { + "type": "assert_invalid", + "line": 146, + "filename": "custom-descriptors.12.wasm", + "module_type": "binary", + "text": "describes with no matching descriptor" + }, + { + "type": "assert_invalid", + "line": 157, + "filename": "custom-descriptors.13.wasm", + "module_type": "binary", + "text": "describes with no matching descriptor" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/cli/custom-descriptors.wast/0.print b/tests/snapshots/cli/custom-descriptors.wast/0.print new file mode 100644 index 0000000000..d6e3ff970f --- /dev/null +++ b/tests/snapshots/cli/custom-descriptors.wast/0.print @@ -0,0 +1,7 @@ +(module + (rec + (type $t1 (;0;) (descriptor $t2 (struct (field i32)))) + (type $t2 (;1;) (describes $t1 (struct (field (ref $f))))) + (type $f (;2;) (func (result i32))) + ) +) diff --git a/tests/snapshots/cli/custom-descriptors.wast/4.print b/tests/snapshots/cli/custom-descriptors.wast/4.print new file mode 100644 index 0000000000..1c7bdd4300 --- /dev/null +++ b/tests/snapshots/cli/custom-descriptors.wast/4.print @@ -0,0 +1,9 @@ +(module + (rec + (type $t1 (;0;) (sub (descriptor $t2 (struct (field i32))))) + (type $t2 (;1;) (sub (describes $t1 (struct (field (ref $f)))))) + (type $t1sub (;2;) (sub $t1 (descriptor $t2sub (struct (field i32) (field f32))))) + (type $t2sub (;3;) (sub $t2 (describes $t1sub (struct (field (ref $f)) (field (ref $f)))))) + (type $f (;4;) (func (result i32))) + ) +)