From c85632b22db1fd7a6b9b7146fc1df204e19d5afb Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Wed, 3 Apr 2024 12:40:12 -0500 Subject: [PATCH] Add `nullexnref` and `noexn` types to exception-handling (#1485) --- crates/wasm-encoder/src/core/types.rs | 7 +++- crates/wasm-mutate/src/module.rs | 1 + crates/wasm-mutate/src/mutators/translate.rs | 1 + crates/wasm-smith/src/core.rs | 16 +++++---- crates/wasmparser/src/readers/core/types.rs | 33 +++++++++++++++++-- crates/wasmparser/src/validator.rs | 2 +- crates/wasmparser/src/validator/core.rs | 3 +- crates/wasmparser/src/validator/types.rs | 12 +++---- crates/wasmprinter/src/lib.rs | 2 ++ crates/wast/src/component/binary.rs | 2 +- crates/wast/src/component/resolve.rs | 3 +- crates/wast/src/core/binary.rs | 6 ++++ crates/wast/src/core/types.rs | 18 ++++++++++ crates/wast/src/lib.rs | 2 ++ crates/wit-component/src/gc.rs | 4 ++- tests/local/exnref/exnref.wast | 3 ++ .../local/exnref/exnref.wast/0.print | 5 +++ 17 files changed, 99 insertions(+), 21 deletions(-) diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs index bfc54ae1e3..85ef0aef9f 100644 --- a/crates/wasm-encoder/src/core/types.rs +++ b/crates/wasm-encoder/src/core/types.rs @@ -450,9 +450,12 @@ pub enum HeapType { /// The unboxed `i31` heap type. I31, - /// The abstract` exception` heap type. + /// The abstract `exception` heap type. Exn, + /// The abstract `noexn` heap type. + NoExn, + /// A concrete Wasm-defined type at the given index. Concrete(u32), } @@ -471,6 +474,7 @@ impl Encode for HeapType { HeapType::Array => sink.push(0x6A), HeapType::I31 => sink.push(0x6C), HeapType::Exn => sink.push(0x69), + HeapType::NoExn => sink.push(0x74), // 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), @@ -496,6 +500,7 @@ impl TryFrom for HeapType { wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, wasmparser::HeapType::Exn => HeapType::Exn, + wasmparser::HeapType::NoExn => HeapType::NoExn, }) } } diff --git a/crates/wasm-mutate/src/module.rs b/crates/wasm-mutate/src/module.rs index 486c9690d2..87653dd76b 100644 --- a/crates/wasm-mutate/src/module.rs +++ b/crates/wasm-mutate/src/module.rs @@ -91,6 +91,7 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result { wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, wasmparser::HeapType::Exn => HeapType::Exn, + wasmparser::HeapType::NoExn => HeapType::NoExn, wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().unwrap()), }, }) diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs index 11fb368c45..58b3a51e29 100644 --- a/crates/wasm-mutate/src/mutators/translate.rs +++ b/crates/wasm-mutate/src/mutators/translate.rs @@ -212,6 +212,7 @@ pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result Ok(HeapType::Array), wasmparser::HeapType::I31 => Ok(HeapType::I31), wasmparser::HeapType::Exn => Ok(HeapType::Exn), + wasmparser::HeapType::NoExn => Ok(HeapType::NoExn), wasmparser::HeapType::Concrete(i) => Ok(HeapType::Concrete( t.remap(Item::Type, i.as_module_index().unwrap())?, )), diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs index b76da6ad67..3180c61885 100644 --- a/crates/wasm-smith/src/core.rs +++ b/crates/wasm-smith/src/core.rs @@ -496,6 +496,8 @@ impl Module { matches!(self.ty(b).composite_type, CompositeType::Func(_)) } + (HT::NoExn, HT::Exn) => true, + // Nothing else matches. (Avoid full wildcard matches so that // adding/modifying variants is easier in the future.) (HT::Concrete(_), _) @@ -508,11 +510,9 @@ impl Module { | (HT::Eq, _) | (HT::Struct, _) | (HT::Array, _) - | (HT::I31, _) => false, - - // TODO: `exn` probably will be its own type hierarchy and will - // probably get `noexn` as well. - (HT::Exn, _) => false, + | (HT::I31, _) + | (HT::Exn, _) + | (HT::NoExn, _) => false, } } @@ -781,7 +781,7 @@ impl Module { HT::Extern => { choices.push(HT::NoExtern); } - HT::Exn | HT::None | HT::NoExtern | HT::NoFunc => {} + HT::Exn | HT::NoExn | HT::None | HT::NoExtern | HT::NoFunc => {} } Ok(*u.choose(&choices)?) } @@ -860,6 +860,9 @@ impl Module { choices.extend(self.func_types.iter().copied().map(HT::Concrete)); choices.push(HT::Func); } + HT::NoExn => { + choices.push(HT::Exn); + } HT::Concrete(mut idx) => { match &self .types @@ -1648,6 +1651,7 @@ impl Module { wasmparser::HeapType::Array => HeapType::Array, wasmparser::HeapType::I31 => HeapType::I31, wasmparser::HeapType::Exn => HeapType::Exn, + wasmparser::HeapType::NoExn => HeapType::NoExn, } } diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs index 6ba9096d2c..0dfcbdc1ba 100644 --- a/crates/wasmparser/src/readers/core/types.rs +++ b/crates/wasmparser/src/readers/core/types.rs @@ -878,6 +878,8 @@ impl std::fmt::Debug for RefType { (false, HeapType::Func) => write!(f, "(ref func)"), (true, HeapType::Exn) => write!(f, "exnref"), (false, HeapType::Exn) => write!(f, "(ref exn)"), + (true, HeapType::NoExn) => write!(f, "nullexnref"), + (false, HeapType::NoExn) => write!(f, "(ref noexn)"), (true, HeapType::Concrete(idx)) => write!(f, "(ref null {idx})"), (false, HeapType::Concrete(idx)) => write!(f, "(ref {idx})"), } @@ -928,6 +930,7 @@ impl RefType { const EXTERN_ABSTYPE: u32 = 0b0011 << 18; const NOEXTERN_ABSTYPE: u32 = 0b0010 << 18; const EXN_ABSTYPE: u32 = 0b0001 << 18; + const NOEXN_ABSTYPE: u32 = 0b1110 << 18; const NONE_ABSTYPE: u32 = 0b0000 << 18; // The `index` is valid only when `concrete == 1`. @@ -972,6 +975,10 @@ impl RefType { /// `exnref`. pub const EXNREF: Self = RefType::EXN.nullable(); + /// A nullable reference to a noexn object aka `(ref null noexn)` aka + /// `nullexnref`. + pub const NULLEXNREF: Self = RefType::NOEXN.nullable(); + /// A non-nullable untyped function reference aka `(ref func)`. pub const FUNC: Self = RefType::from_u32(Self::FUNC_ABSTYPE); @@ -1005,6 +1012,9 @@ impl RefType { /// A non-nullable reference to an exn object aka `(ref exn)`. pub const EXN: Self = RefType::from_u32(Self::EXN_ABSTYPE); + /// A non-nullable reference to a noexn object aka `(ref noexn)`. + pub const NOEXN: Self = RefType::from_u32(Self::NOEXN_ABSTYPE); + const fn can_represent_type_index(index: u32) -> bool { index & Self::INDEX_MASK == index } @@ -1045,6 +1055,7 @@ impl RefType { | Self::NOEXTERN_ABSTYPE | Self::NONE_ABSTYPE | Self::EXN_ABSTYPE + | Self::NOEXN_ABSTYPE ) ); @@ -1083,6 +1094,7 @@ impl RefType { HeapType::Array => Some(Self::from_u32(nullable32 | Self::ARRAY_ABSTYPE)), HeapType::I31 => Some(Self::from_u32(nullable32 | Self::I31_ABSTYPE)), HeapType::Exn => Some(Self::from_u32(nullable32 | Self::EXN_ABSTYPE)), + HeapType::NoExn => Some(Self::from_u32(nullable32 | Self::NOEXN_ABSTYPE)), } } @@ -1179,6 +1191,7 @@ impl RefType { Self::ARRAY_ABSTYPE => HeapType::Array, Self::I31_ABSTYPE => HeapType::I31, Self::EXN_ABSTYPE => HeapType::Exn, + Self::NOEXN_ABSTYPE => HeapType::NoExn, _ => unreachable!(), } } @@ -1200,6 +1213,7 @@ impl RefType { (true, HeapType::Array) => "arrayref", (true, HeapType::I31) => "i31ref", (true, HeapType::Exn) => "exnref", + (true, HeapType::NoExn) => "nullexnref", (false, HeapType::Func) => "(ref func)", (false, HeapType::Extern) => "(ref extern)", (false, HeapType::Concrete(_)) => "(ref $type)", @@ -1212,6 +1226,7 @@ impl RefType { (false, HeapType::Array) => "(ref array)", (false, HeapType::I31) => "(ref i31)", (false, HeapType::Exn) => "(ref exn)", + (false, HeapType::NoExn) => "(ref noexn)", } } } @@ -1297,13 +1312,20 @@ pub enum HeapType { /// /// Introduced in the exception-handling proposal. Exn, + + /// The abstract `noexn` heap type. + /// + /// The common subtype (a.k.a. bottom) of all exception types. + /// + /// Introduced in the exception-handling proposal. + NoExn, } impl ValType { pub(crate) fn is_valtype_byte(byte: u8) -> bool { match byte { 0x7F | 0x7E | 0x7D | 0x7C | 0x7B | 0x70 | 0x6F | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 - | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69 => true, + | 0x74 | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69 => true, _ => false, } } @@ -1348,8 +1370,8 @@ impl<'a> FromReader<'a> for ValType { reader.position += 1; Ok(ValType::V128) } - 0x70 | 0x6F | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C - | 0x69 => Ok(ValType::Ref(reader.read()?)), + 0x70 | 0x6F | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 | 0x73 | 0x74 | 0x6D | 0x6B | 0x6A + | 0x6C | 0x69 => Ok(ValType::Ref(reader.read()?)), _ => bail!(reader.original_position(), "invalid value type"), } } @@ -1369,6 +1391,7 @@ impl<'a> FromReader<'a> for RefType { 0x6A => Ok(RefType::ARRAY.nullable()), 0x6C => Ok(RefType::I31.nullable()), 0x69 => Ok(RefType::EXN.nullable()), + 0x74 => Ok(RefType::NOEXN.nullable()), byte @ (0x63 | 0x64) => { let nullable = byte == 0x63; let pos = reader.original_position(); @@ -1427,6 +1450,10 @@ impl<'a> FromReader<'a> for HeapType { reader.position += 1; Ok(HeapType::Exn) } + 0x74 => { + reader.position += 1; + Ok(HeapType::NoExn) + } _ => { let idx = match u32::try_from(reader.read_var_s33()?) { Ok(idx) => idx, diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 5e7833e722..47e429cda4 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -358,7 +358,7 @@ impl WasmFeatures { } // These types were added in the exception-handling proposal. - (HeapType::Exn, _) => { + (HeapType::Exn | HeapType::NoExn, _) => { if self.exceptions { Ok(()) } else { diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index 6fc077c178..c0f983de75 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -982,7 +982,8 @@ impl Module { | HeapType::Struct | HeapType::Array | HeapType::I31 - | HeapType::Exn => return Ok(()), + | HeapType::Exn + | HeapType::NoExn => return Ok(()), HeapType::Concrete(type_index) => type_index, }; match type_index { diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs index 5f8791e01d..d82ba55de2 100644 --- a/crates/wasmparser/src/validator/types.rs +++ b/crates/wasmparser/src/validator/types.rs @@ -2695,6 +2695,8 @@ impl TypeList { matches!(subtype(b_group, b).composite_type, CompositeType::Func(_)) } + (HT::NoExn, HT::Exn) => true, + // Nothing else matches. (Avoid full wildcard matches so that // adding/modifying variants is easier in the future.) (HT::Concrete(_), _) @@ -2707,11 +2709,9 @@ impl TypeList { | (HT::Eq, _) | (HT::Struct, _) | (HT::Array, _) - | (HT::I31, _) => false, - - // TODO: this probably isn't right, this is probably related to some - // gc type. - (HT::Exn, _) => false, + | (HT::I31, _) + | (HT::Exn, _) + | (HT::NoExn, _) => false, } } @@ -2749,7 +2749,7 @@ impl TypeList { | HeapType::Array | HeapType::I31 | HeapType::None => HeapType::Any, - HeapType::Exn => HeapType::Exn, + HeapType::Exn | HeapType::NoExn => HeapType::Exn, } } diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs index 26962f3956..514530f9a3 100644 --- a/crates/wasmprinter/src/lib.rs +++ b/crates/wasmprinter/src/lib.rs @@ -969,6 +969,7 @@ impl Printer { RefType::STRUCT => self.result.push_str("structref"), RefType::ARRAY => self.result.push_str("arrayref"), RefType::EXN => self.result.push_str("exnref"), + RefType::NOEXN => self.result.push_str("nullexnref"), _ => { self.result.push_str("(ref null "); self.print_heaptype(state, ty.heap_type())?; @@ -996,6 +997,7 @@ impl Printer { HeapType::Array => self.result.push_str("array"), HeapType::I31 => self.result.push_str("i31"), HeapType::Exn => self.result.push_str("exn"), + HeapType::NoExn => self.result.push_str("noexn"), HeapType::Concrete(i) => { self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?; } diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs index 6ee11e0259..2629146c48 100644 --- a/crates/wast/src/component/binary.rs +++ b/crates/wast/src/component/binary.rs @@ -597,7 +597,7 @@ impl From> for wasm_encoder::HeapType { match r { core::HeapType::Func => Self::Func, core::HeapType::Extern => Self::Extern, - core::HeapType::Exn => { + core::HeapType::Exn | core::HeapType::NoExn => { todo!("encoding of exceptions proposal types not yet implemented") } core::HeapType::Concrete(Index::Num(i, _)) => Self::Concrete(i), diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs index c0122ef73f..0f33df564a 100644 --- a/crates/wast/src/component/resolve.rs +++ b/crates/wast/src/component/resolve.rs @@ -531,7 +531,8 @@ impl<'a> Resolver<'a> { | core::HeapType::Struct | core::HeapType::None | core::HeapType::NoFunc - | core::HeapType::NoExtern => {} + | core::HeapType::NoExtern + | core::HeapType::NoExn => {} core::HeapType::Concrete(id) => { self.resolve_ns(id, Ns::Type)?; } diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs index 8531e81e5c..37e7d64028 100644 --- a/crates/wast/src/core/binary.rs +++ b/crates/wast/src/core/binary.rs @@ -295,6 +295,7 @@ impl<'a> Encode for HeapType<'a> { HeapType::I31 => e.push(0x6c), HeapType::NoFunc => e.push(0x73), HeapType::NoExtern => e.push(0x72), + HeapType::NoExn => e.push(0x74), HeapType::None => e.push(0x71), // Note that this is encoded as a signed leb128 so be sure to cast // to an i64 first @@ -349,6 +350,11 @@ impl<'a> Encode for RefType<'a> { nullable: true, heap: HeapType::NoExtern, } => e.push(0x72), + // The 'nullexnref' binary abbreviation + RefType { + nullable: true, + heap: HeapType::NoExn, + } => e.push(0x74), // The 'nullref' binary abbreviation RefType { nullable: true, diff --git a/crates/wast/src/core/types.rs b/crates/wast/src/core/types.rs index ebae2e42b4..3cb1930deb 100644 --- a/crates/wast/src/core/types.rs +++ b/crates/wast/src/core/types.rs @@ -86,6 +86,8 @@ pub enum HeapType<'a> { NoExtern, /// The bottom type of the anyref hierarchy. Part of the GC proposal. None, + /// The bottom type of the exnref hierarchy. Part of the exceptions proposal. + NoExn, /// 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>), @@ -124,6 +126,9 @@ impl<'a> Parse<'a> for HeapType<'a> { } else if l.peek::()? { parser.parse::()?; Ok(HeapType::NoExtern) + } else if l.peek::()? { + parser.parse::()?; + Ok(HeapType::NoExn) } else if l.peek::()? { parser.parse::()?; Ok(HeapType::None) @@ -147,6 +152,7 @@ impl<'a> Peek for HeapType<'a> { || kw::i31::peek(cursor)? || kw::nofunc::peek(cursor)? || kw::noextern::peek(cursor)? + || kw::noexn::peek(cursor)? || kw::none::peek(cursor)? || (LParen::peek(cursor)? && kw::r#type::peek2(cursor)?)) } @@ -251,6 +257,14 @@ impl<'a> RefType<'a> { heap: HeapType::None, } } + + /// A `nullexnref` as an abbreviation for `(ref null noexn)`. + pub fn nullexnref() -> Self { + RefType { + nullable: true, + heap: HeapType::NoExn, + } + } } impl<'a> Parse<'a> for RefType<'a> { @@ -286,6 +300,9 @@ impl<'a> Parse<'a> for RefType<'a> { } else if l.peek::()? { parser.parse::()?; Ok(RefType::nullexternref()) + } else if l.peek::()? { + parser.parse::()?; + Ok(RefType::nullexnref()) } else if l.peek::()? { parser.parse::()?; Ok(RefType::nullref()) @@ -327,6 +344,7 @@ impl<'a> Peek for RefType<'a> { || kw::i31ref::peek(cursor)? || kw::nullfuncref::peek(cursor)? || kw::nullexternref::peek(cursor)? + || kw::nullexnref::peek(cursor)? || kw::nullref::peek(cursor)? || (LParen::peek(cursor)? && kw::r#ref::peek2(cursor)?)) } diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs index bb16574177..e12f9bda44 100644 --- a/crates/wast/src/lib.rs +++ b/crates/wast/src/lib.rs @@ -447,10 +447,12 @@ pub mod kw { custom_keyword!(nan_canonical = "nan:canonical"); custom_keyword!(nofunc); custom_keyword!(noextern); + custom_keyword!(noexn); custom_keyword!(none); custom_keyword!(null); custom_keyword!(nullfuncref); custom_keyword!(nullexternref); + custom_keyword!(nullexnref); custom_keyword!(nullref); custom_keyword!(offset); custom_keyword!(outer); diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs index 5aacf8eb9b..b0fc67c0d7 100644 --- a/crates/wit-component/src/gc.rs +++ b/crates/wit-component/src/gc.rs @@ -496,7 +496,8 @@ impl<'a> Module<'a> { | HeapType::Struct | HeapType::Array | HeapType::I31 - | HeapType::Exn => {} + | HeapType::Exn + | HeapType::NoExn => {} HeapType::Concrete(i) => self.ty(i.as_module_index().unwrap()), } } @@ -1134,6 +1135,7 @@ impl Encoder { HeapType::Array => wasm_encoder::HeapType::Array, HeapType::I31 => wasm_encoder::HeapType::I31, HeapType::Exn => wasm_encoder::HeapType::Exn, + HeapType::NoExn => wasm_encoder::HeapType::NoExn, HeapType::Concrete(idx) => { wasm_encoder::HeapType::Concrete(self.types.remap(idx.as_module_index().unwrap())) } diff --git a/tests/local/exnref/exnref.wast b/tests/local/exnref/exnref.wast index 421eca772e..e0b51e16dd 100644 --- a/tests/local/exnref/exnref.wast +++ b/tests/local/exnref/exnref.wast @@ -2,4 +2,7 @@ (func (param exnref)) (func (param (ref null exn))) (func (param (ref exn))) + (func (param nullexnref)) + (func (param (ref null noexn))) + (func (param (ref noexn))) ) diff --git a/tests/snapshots/local/exnref/exnref.wast/0.print b/tests/snapshots/local/exnref/exnref.wast/0.print index 263d386874..168b079741 100644 --- a/tests/snapshots/local/exnref/exnref.wast/0.print +++ b/tests/snapshots/local/exnref/exnref.wast/0.print @@ -1,7 +1,12 @@ (module (type (;0;) (func (param exnref))) (type (;1;) (func (param (ref exn)))) + (type (;2;) (func (param nullexnref))) + (type (;3;) (func (param (ref noexn)))) (func (;0;) (type 0) (param exnref)) (func (;1;) (type 0) (param exnref)) (func (;2;) (type 1) (param (ref exn))) + (func (;3;) (type 2) (param nullexnref)) + (func (;4;) (type 2) (param nullexnref)) + (func (;5;) (type 3) (param (ref noexn))) )