diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs index 68cffa3f9..07bbb311f 100644 --- a/crates/wasm-compose/src/encoding.rs +++ b/crates/wasm-compose/src/encoding.rs @@ -288,7 +288,7 @@ impl<'a> TypeEncoder<'a> { wasmparser::HeapType::Struct => HeapType::Struct, wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, - wasmparser::HeapType::TypedFunc(i) => HeapType::TypedFunc(i.into()), + wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i.into()), }, } } diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs index 5ece76e01..4deffa8a9 100644 --- a/crates/wasm-encoder/src/core/types.rs +++ b/crates/wasm-encoder/src/core/types.rs @@ -141,8 +141,8 @@ pub enum HeapType { Array, /// The i31 heap type. I31, - /// Function of the type at the given index. - TypedFunc(u32), + /// User defined type at the given index. + Indexed(u32), } impl Encode for HeapType { @@ -160,7 +160,7 @@ impl Encode for HeapType { HeapType::I31 => sink.push(0x6A), // Note that this is encoded as a signed type rather than unsigned // as it's decoded as an s33 - HeapType::TypedFunc(i) => i64::from(*i).encode(sink), + HeapType::Indexed(i) => i64::from(*i).encode(sink), } } } diff --git a/crates/wasm-mutate/src/module.rs b/crates/wasm-mutate/src/module.rs index b67b0b373..0dd49320a 100644 --- a/crates/wasm-mutate/src/module.rs +++ b/crates/wasm-mutate/src/module.rs @@ -98,7 +98,7 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result { wasmparser::HeapType::Struct => HeapType::Struct, wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, - wasmparser::HeapType::TypedFunc(i) => HeapType::TypedFunc(i.into()), + wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i.into()), }, }) } diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs index 114ae4553..bac994595 100644 --- a/crates/wasm-mutate/src/mutators/translate.rs +++ b/crates/wasm-mutate/src/mutators/translate.rs @@ -211,8 +211,8 @@ pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result Ok(HeapType::Struct), wasmparser::HeapType::Array => Ok(HeapType::Array), wasmparser::HeapType::I31 => Ok(HeapType::I31), - wasmparser::HeapType::TypedFunc(i) => { - Ok(HeapType::TypedFunc(t.remap(Item::Type, (*i).into())?)) + wasmparser::HeapType::Indexed(i) => { + Ok(HeapType::Indexed(t.remap(Item::Type, (*i).into())?)) } } } diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index 867e912bb..6fb961000 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -1654,7 +1654,7 @@ fn convert_reftype(ty: wasmparser::RefType) -> RefType { wasmparser::HeapType::Struct => HeapType::Struct, wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, - wasmparser::HeapType::TypedFunc(i) => HeapType::TypedFunc(i.into()), + wasmparser::HeapType::Indexed(i) => HeapType::Indexed(i.into()), }, } } diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs index ea655d943..fe1b8ff57 100644 --- a/crates/wasmparser/src/readers/core/types.rs +++ b/crates/wasmparser/src/readers/core/types.rs @@ -219,8 +219,8 @@ impl Debug for RefType { (false, HeapType::Extern) => write!(f, "(ref extern)"), (true, HeapType::Func) => write!(f, "funcref"), (false, HeapType::Func) => write!(f, "(ref func)"), - (true, HeapType::TypedFunc(idx)) => write!(f, "(ref null {idx})"), - (false, HeapType::TypedFunc(idx)) => write!(f, "(ref {idx})"), + (true, HeapType::Indexed(idx)) => write!(f, "(ref null {idx})"), + (false, HeapType::Indexed(idx)) => write!(f, "(ref {idx})"), } } } @@ -333,15 +333,6 @@ impl RefType { const fn from_u32(x: u32) -> Self { debug_assert!(x & (0b11111111 << 24) == 0); - // if indexed, kind must be struct/array/func - debug_assert!( - x & Self::INDEXED_BIT == 0 - || matches!( - x & Self::KIND_MASK, - Self::FUNC_KIND | Self::ARRAY_KIND | Self::STRUCT_KIND - ) - ); - // if not indexed, type must be any/eq/i31/struct/array/func/extern/nofunc/noextern/none debug_assert!( x & Self::INDEXED_BIT != 0 @@ -409,7 +400,7 @@ impl RefType { pub const fn new(nullable: bool, heap_type: HeapType) -> Option { let nullable32 = Self::NULLABLE_BIT * nullable as u32; match heap_type { - HeapType::TypedFunc(index) => RefType::indexed_func(nullable, index), + HeapType::Indexed(index) => RefType::indexed(nullable, 0, index), // 0 bc we don't know the kind HeapType::Func => Some(Self::from_u32(nullable32 | Self::FUNC_TYPE)), HeapType::Extern => Some(Self::from_u32(nullable32 | Self::EXTERN_TYPE)), HeapType::Any => Some(Self::from_u32(nullable32 | Self::ANY_TYPE)), @@ -435,7 +426,7 @@ impl RefType { /// If this is a reference to a typed function, get its type index. pub const fn type_index(&self) -> Option { - if self.is_typed_func_ref() { + if self.is_indexed_type_ref() { Some(self.as_u32() & Self::INDEX_MASK) } else { None @@ -471,10 +462,7 @@ impl RefType { pub fn heap_type(&self) -> HeapType { let s = self.as_u32(); if self.is_indexed_type_ref() { - match s & Self::KIND_MASK { - Self::FUNC_KIND => HeapType::TypedFunc(self.type_index().unwrap()), - _ => unreachable!(), - } + HeapType::Indexed(self.type_index().unwrap()) } else { match s & Self::TYPE_MASK { Self::FUNC_TYPE => HeapType::Func, @@ -498,7 +486,7 @@ impl RefType { match (self.is_nullable(), self.heap_type()) { (true, HeapType::Func) => "funcref", (true, HeapType::Extern) => "externref", - (true, HeapType::TypedFunc(_)) => "(ref null $type)", + (true, HeapType::Indexed(_)) => "(ref null $type)", (true, HeapType::Any) => "anyref", (true, HeapType::None) => "nullref", (true, HeapType::NoExtern) => "nullexternref", @@ -509,7 +497,7 @@ impl RefType { (true, HeapType::I31) => "i31ref", (false, HeapType::Func) => "(ref func)", (false, HeapType::Extern) => "(ref extern)", - (false, HeapType::TypedFunc(_)) => "(ref $type)", + (false, HeapType::Indexed(_)) => "(ref $type)", (false, HeapType::Any) => "(ref any)", (false, HeapType::None) => "(ref none)", (false, HeapType::NoExtern) => "(ref noextern)", @@ -553,7 +541,7 @@ impl fmt::Display for RefType { let s = match (self.is_nullable(), self.heap_type()) { (true, HeapType::Func) => "funcref", (true, HeapType::Extern) => "externref", - (true, HeapType::TypedFunc(i)) => return write!(f, "(ref null {i})"), + (true, HeapType::Indexed(i)) => return write!(f, "(ref null {i})"), (true, HeapType::Any) => "anyref", (true, HeapType::None) => "nullref", (true, HeapType::NoExtern) => "nullexternref", @@ -564,7 +552,7 @@ impl fmt::Display for RefType { (true, HeapType::I31) => "i31ref", (false, HeapType::Func) => "(ref func)", (false, HeapType::Extern) => "(ref extern)", - (false, HeapType::TypedFunc(i)) => return write!(f, "(ref {i})"), + (false, HeapType::Indexed(i)) => return write!(f, "(ref {i})"), (false, HeapType::Any) => "(ref any)", (false, HeapType::None) => "(ref none)", (false, HeapType::NoExtern) => "(ref noextern)", @@ -583,7 +571,7 @@ impl fmt::Display for RefType { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum HeapType { /// User defined type at the given index. - TypedFunc(u32), + Indexed(u32), /// Untyped (any) function. Func, /// External heap type. @@ -654,10 +642,10 @@ impl<'a> FromReader<'a> for HeapType { let idx = match u32::try_from(reader.read_var_s33()?) { Ok(idx) => idx, Err(_) => { - bail!(reader.original_position(), "invalid function heap type",); + bail!(reader.original_position(), "invalid indexed ref heap type"); } }; - Ok(HeapType::TypedFunc(idx)) + Ok(HeapType::Indexed(idx)) } } } diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 86d8b3de5..945087a4f 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -271,7 +271,7 @@ impl WasmFeatures { (_, false) => { Err("function references required for non-nullable types") } - (HeapType::TypedFunc(_), _) => { + (HeapType::Indexed(_), _) => { Err("function references required for index reference types") } _ => Ok(()), diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 46c06cfb0..c88467768 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -1500,7 +1500,7 @@ impl ComponentState { crate::OuterAliasKind::Type => { let ty = if count == 0 { // Local alias, check the local module state - state.type_at(index, offset)? + state.type_id_at(index, offset)? } else { // Otherwise, check the enclosing component state let component = diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 3b87e959a..3bae5365b 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -9,8 +9,9 @@ use crate::limits::*; use crate::validator::core::arc::MaybeOwned; use crate::{ BinaryReaderError, ConstExpr, Data, DataKind, Element, ElementKind, ExternalKind, FuncType, - Global, GlobalType, HeapType, MemoryType, RefType, Result, Table, TableInit, TableType, - TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType, WasmModuleResources, + Global, GlobalType, HeapType, MemoryType, RefType, Result, StorageType, Table, TableInit, + TableType, TagType, TypeRef, ValType, VisitOperator, WasmFeatures, WasmFuncType, + WasmModuleResources, }; use indexmap::IndexMap; use std::mem; @@ -647,20 +648,24 @@ impl Module { Ok(()) } - pub fn type_at(&self, idx: u32, offset: usize) -> Result { + pub fn type_id_at(&self, idx: u32, offset: usize) -> Result { self.types .get(idx as usize) .copied() .ok_or_else(|| format_err!(offset, "unknown type {idx}: type index out of bounds")) } + fn type_at<'a>(&self, types: &'a TypeList, idx: u32, offset: usize) -> Result<&'a Type> { + self.type_id_at(idx, offset).map(|type_id| &types[type_id]) + } + fn func_type_at<'a>( &self, type_index: u32, types: &'a TypeList, offset: usize, ) -> Result<&'a FuncType> { - types[self.type_at(type_index, offset)?] + types[self.type_id_at(type_index, offset)?] .as_func_type() .ok_or_else(|| format_err!(offset, "type index {type_index} is not a function type")) } @@ -816,9 +821,9 @@ impl Module { | HeapType::Struct | HeapType::Array | HeapType::I31 => (), - HeapType::TypedFunc(type_index) => { + HeapType::Indexed(type_index) => { // Just check that the index is valid - self.type_at(type_index, offset)?; + self.type_id_at(type_index, offset)?; } } Ok(()) @@ -829,19 +834,34 @@ impl Module { (ValType::Ref(rt1), ValType::Ref(rt2)) => { rt1.is_nullable() == rt2.is_nullable() && match (rt1.heap_type(), rt2.heap_type()) { - (HeapType::Func, HeapType::Func) => true, - (HeapType::Extern, HeapType::Extern) => true, - (HeapType::TypedFunc(n1), HeapType::TypedFunc(n2)) => { - let n1 = self.func_type_at(n1.into(), types, 0).unwrap(); - let n2 = self.func_type_at(n2.into(), types, 0).unwrap(); - self.eq_fns(n1, n2, types) + (HeapType::Indexed(n1), HeapType::Indexed(n2)) => { + self.eq_indexed_types(n1, n2, types) } - (_, _) => false, + (h1, h2) => h1 == h2, } } _ => ty1 == ty2, } } + + fn eq_indexed_types(&self, n1: u32, n2: u32, types: &TypeList) -> bool { + let n1 = self.type_at(types, n1.into(), 0).unwrap(); + let n2 = self.type_at(types, n2.into(), 0).unwrap(); + match (n1, n2) { + (Type::Func(f1), Type::Func(f2)) => self.eq_fns(f1, f2, types), + (Type::Array(a1), Type::Array(a2)) => { + a1.mutable == a2.mutable + && match (a1.element_type, a2.element_type) { + (StorageType::Val(vt1), StorageType::Val(vt2)) => { + self.eq_valtypes(vt1, vt2, types) + } + (st1, st2) => st1 == st2, + } + } + _ => false, + } + } + fn eq_fns(&self, f1: &impl WasmFuncType, f2: &impl WasmFuncType, types: &TypeList) -> bool { f1.len_inputs() == f2.len_inputs() && f2.len_outputs() == f2.len_outputs() @@ -855,6 +875,9 @@ impl Module { .all(|(t1, t2)| self.eq_valtypes(t1, t2, types)) } + /// Check that a value of type ty1 is assignable to a variable / table element of type ty2. + /// E.g. a non-nullable reference can be assigned to a nullable reference, but not vice versa. + /// Or an indexed func ref is assignable to a generic func ref, but not vice versa. pub(crate) fn matches(&self, ty1: ValType, ty2: ValType, types: &TypeList) -> bool { fn matches_null(null1: bool, null2: bool) -> bool { (null1 == null2) || null2 @@ -862,13 +885,33 @@ impl Module { let matches_heap = |ty1: HeapType, ty2: HeapType, types: &TypeList| -> bool { match (ty1, ty2) { - (HeapType::TypedFunc(n1), HeapType::TypedFunc(n2)) => { + (HeapType::Indexed(n1), HeapType::Indexed(n2)) => { // Check whether the defined types are (structurally) equivalent. - let n1 = self.func_type_at(n1.into(), types, 0).unwrap(); - let n2 = self.func_type_at(n2.into(), types, 0).unwrap(); - self.eq_fns(n1, n2, types) + let n1 = self.type_at(types, n1.into(), 0); + let n2 = self.type_at(types, n2.into(), 0); + match (n1, n2) { + (Ok(Type::Func(n1)), Ok(Type::Func(n2))) => self.eq_fns(n1, n2, types), + (Ok(Type::Array(n1)), Ok(Type::Array(n2))) => { + (n1.mutable == n2.mutable || n2.mutable) + && match (n1.element_type, n2.element_type) { + (StorageType::Val(vt1), StorageType::Val(vt2)) => { + self.matches(vt1, vt2, types) + } + (st1, st2) => st1 == st2, + } + } + _ => false, + } + } + (HeapType::Indexed(n1), HeapType::Func) => { + self.func_type_at(n1.into(), types, 0).is_ok() + } + (HeapType::Indexed(n1), HeapType::Array) => { + match self.type_at(types, n1.into(), 0) { + Ok(Type::Array(_)) => true, + _ => false, + } } - (HeapType::TypedFunc(_), HeapType::Func) => true, (_, _) => ty1 == ty2, } }; diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs index aeccab6e3..a0fa1a10a 100644 --- a/crates/wasmparser/src/validator/operators.rs +++ b/crates/wasmparser/src/validator/operators.rs @@ -1272,14 +1272,14 @@ where Ok(()) } fn visit_call_ref(&mut self, type_index: u32) -> Self::Output { - let hty = HeapType::TypedFunc(type_index); + let hty = HeapType::Indexed(type_index); self.resources .check_heap_type(hty, &self.features, self.offset)?; // If `None` is popped then that means a "bottom" type was popped which // is always considered equivalent to the `hty` tag. if let Some(rt) = self.pop_ref()? { - let expected = - RefType::new(true, hty).expect("existing heap types should be within our limits"); + let expected = RefType::indexed_func(true, type_index) + .expect("existing heap types should be within our limits"); if !self .resources .matches(ValType::Ref(rt), ValType::Ref(expected)) @@ -2291,9 +2291,8 @@ where // FIXME(#924) this should not be conditional based on enabled // proposals. if self.features.function_references { - let heap_type = HeapType::TypedFunc(type_index); self.push_operand( - RefType::new(false, heap_type) + RefType::indexed_func(false, type_index) .expect("our limits on number of types should fit into ref type"), )?; } else { diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index 6b3c71025..3e49ed2d3 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -798,7 +798,7 @@ impl Printer { HeapType::Struct => self.result.push_str("struct"), HeapType::Array => self.result.push_str("array"), HeapType::I31 => self.result.push_str("i31"), - HeapType::TypedFunc(i) => self.result.push_str(&format!("{}", u32::from(i))), + HeapType::Indexed(i) => self.result.push_str(&format!("{}", u32::from(i))), } Ok(()) } diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index dc9539d7c..d58b9e62b 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -589,7 +589,7 @@ impl From> for wasm_encoder::HeapType { match r { core::HeapType::Func => Self::Func, core::HeapType::Extern => Self::Extern, - core::HeapType::Index(Index::Num(i, _)) => Self::TypedFunc(i), + core::HeapType::Index(Index::Num(i, _)) => Self::Indexed(i), core::HeapType::Index(_) => panic!("unresolved index"), core::HeapType::Any | core::HeapType::Eq diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index 1226ff5a2..2f5ac0125 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -499,7 +499,7 @@ impl<'a> Module<'a> { | HeapType::Struct | HeapType::Array | HeapType::I31 => {} - HeapType::TypedFunc(i) => self.ty(i.into()), + HeapType::Indexed(i) => self.ty(i.into()), } } @@ -1141,8 +1141,8 @@ impl Encoder { HeapType::Struct => wasm_encoder::HeapType::Struct, HeapType::Array => wasm_encoder::HeapType::Array, HeapType::I31 => wasm_encoder::HeapType::I31, - HeapType::TypedFunc(idx) => { - wasm_encoder::HeapType::TypedFunc(self.types.remap(idx.into()).try_into().unwrap()) + HeapType::Indexed(idx) => { + wasm_encoder::HeapType::Indexed(self.types.remap(idx.into()).try_into().unwrap()) } } }