Skip to content

Commit

Permalink
Rename HeapType::TypedFunc(u32) to Indexed(u32) (#1042)
Browse files Browse the repository at this point in the history
`HeapType::TypedFunc(u32)` has been (as of [typed references](https://github.com/WebAssembly/function-references) proposal) used to refer function types defined in the module.

It is parsed from `(ref null? {index})`. With introduction of new indexed types for GC, there's unfortunately no way to tell what type the index is pointing to while parsing: they used to be all `func` types, but now can also be `array`, `struct` and more.

Renaming HeapType::TypedFunc(u32) to Indexed(u32) reflects this change.

Summary of the changes:
- Match RefTypes where one or both has HeapType::Indexed.
- `RefType::new()` will now create indexed RefType with unknown kind.
- Use `RefType::indexed_func()` instead of `RefType::new()` for indexed func refs.
- Add `Self::ARRAY_KIND` to the list of matched RefType kinds - when getting HeapType of a RefType.
- Check if two indexed types are equal.
  • Loading branch information
imikushin committed May 17, 2023
1 parent 6d24468 commit 286b880
Show file tree
Hide file tree
Showing 13 changed files with 92 additions and 62 deletions.
2 changes: 1 addition & 1 deletion crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
},
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/wasm-encoder/src/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-mutate/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result<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()),
},
})
}
Expand Down
4 changes: 2 additions & 2 deletions crates/wasm-mutate/src/mutators/translate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result<HeapT
wasmparser::HeapType::Struct => 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())?))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasm-smith/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
},
}
}
Expand Down
36 changes: 12 additions & 24 deletions crates/wasmparser/src/readers/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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})"),
}
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -409,7 +400,7 @@ impl RefType {
pub const fn new(nullable: bool, heap_type: HeapType) -> Option<Self> {
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)),
Expand All @@ -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<u32> {
if self.is_typed_func_ref() {
if self.is_indexed_type_ref() {
Some(self.as_u32() & Self::INDEX_MASK)
} else {
None
Expand Down Expand Up @@ -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,
Expand All @@ -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",
Expand All @@ -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)",
Expand Down Expand Up @@ -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",
Expand All @@ -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)",
Expand All @@ -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.
Expand Down Expand Up @@ -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))
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmparser/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(()),
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
79 changes: 61 additions & 18 deletions crates/wasmparser/src/validator/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -647,20 +648,24 @@ impl Module {
Ok(())
}

pub fn type_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
pub fn type_id_at(&self, idx: u32, offset: usize) -> Result<TypeId> {
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"))
}
Expand Down Expand Up @@ -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(())
Expand All @@ -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()
Expand All @@ -855,20 +875,43 @@ 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
}

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,
}
};
Expand Down
9 changes: 4 additions & 5 deletions crates/wasmparser/src/validator/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmprinter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
}
Expand Down
2 changes: 1 addition & 1 deletion crates/wast/src/component/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ impl From<core::HeapType<'_>> 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
Expand Down
6 changes: 3 additions & 3 deletions crates/wit-component/src/gc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()),
}
}

Expand Down Expand Up @@ -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())
}
}
}
Expand Down

0 comments on commit 286b880

Please sign in to comment.