diff --git a/crates/wast/src/ast/expr.rs b/crates/wast/src/ast/expr.rs index 1febe48..f0b563c 100644 --- a/crates/wast/src/ast/expr.rs +++ b/crates/wast/src/ast/expr.rs @@ -1,4 +1,4 @@ -use crate::ast::{self, kw}; +use crate::ast::{self, kw, RefType}; use crate::parser::{Parse, Parser, Result}; use std::mem; @@ -411,8 +411,8 @@ instructions! { TableSize(TableArg<'a>) : [0xfc, 0x10] : "table.size", TableGrow(TableArg<'a>) : [0xfc, 0x0f] : "table.grow", - RefNull : [0xd0] : "ref.null", - RefIsNull : [0xd1] : "ref.is_null", + RefNull(RefType<'a>) : [0xd0] : "ref.null", + RefIsNull(RefType<'a>) : [0xd1] : "ref.is_null", RefHost(u32) : [0xff] : "ref.host", // only used in test harness RefFunc(ast::Index<'a>) : [0xd2] : "ref.func", diff --git a/crates/wast/src/ast/mod.rs b/crates/wast/src/ast/mod.rs index 35eaf29..07cd6df 100644 --- a/crates/wast/src/ast/mod.rs +++ b/crates/wast/src/ast/mod.rs @@ -348,6 +348,8 @@ pub mod kw { custom_keyword!(exn); custom_keyword!(exnref); custom_keyword!(export); + custom_keyword!(r#extern = "extern"); + custom_keyword!(externref); custom_keyword!(eq); custom_keyword!(eqref); custom_keyword!(f32); diff --git a/crates/wast/src/ast/table.rs b/crates/wast/src/ast/table.rs index a59bb3a..eee4b24 100644 --- a/crates/wast/src/ast/table.rs +++ b/crates/wast/src/ast/table.rs @@ -208,12 +208,12 @@ impl<'a> ElemPayload<'a> { let func = parser.parens(|p| match p.parse::>()? { Some(_) => { if parser.peek::() { - parser.parens(parse_ref_func) + parser.parens(|p| parse_ref_func(p, ty)) } else { - parse_ref_func(parser) + parse_ref_func(parser, ty) } } - None => parse_ref_func(p), + None => parse_ref_func(parser, ty), })?; exprs.push(func); } @@ -221,10 +221,14 @@ impl<'a> ElemPayload<'a> { } } -fn parse_ref_func<'a>(parser: Parser<'a>) -> Result>> { +fn parse_ref_func<'a>(parser: Parser<'a>, ty: ast::TableElemType) -> Result>> { let mut l = parser.lookahead1(); if l.peek::() { parser.parse::()?; + let null_ty: ast::RefType = parser.parse()?; + if null_ty != ty.into() { + return Err(parser.error("elem segment item doesn't match elem segment type")); + } Ok(None) } else if l.peek::() { parser.parse::()?; diff --git a/crates/wast/src/ast/types.rs b/crates/wast/src/ast/types.rs index e0afc46..797792e 100644 --- a/crates/wast/src/ast/types.rs +++ b/crates/wast/src/ast/types.rs @@ -12,14 +12,7 @@ pub enum ValType<'a> { V128, I8, I16, - Funcref, - Anyref, - Nullref, - Exnref, - Ref(ast::Index<'a>), - Optref(ast::Index<'a>), - Eqref, - I31ref, + Ref(RefType<'a>), Rtt(ast::Index<'a>), } @@ -49,52 +42,27 @@ impl<'a> Parse<'a> for ValType<'a> { Ok(ValType::I16) } else if l.peek::() { parser.parse::()?; - Ok(ValType::Funcref) + Ok(ValType::Ref(RefType::Func)) } else if l.peek::() { parser.parse::()?; - Ok(ValType::Funcref) + Ok(ValType::Ref(RefType::Func)) + } else if l.peek::() { + parser.parse::()?; + Ok(ValType::Ref(RefType::Extern)) } else if l.peek::() { + // Parse `anyref` as an alias of `externref` until all tests are + // ported to use the new name parser.parse::()?; - Ok(ValType::Anyref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::Nullref) + Ok(ValType::Ref(RefType::Extern)) } else if l.peek::() { parser.parens(|p| { let mut l = parser.lookahead1(); if l.peek::() { p.parse::()?; - - let mut l = parser.lookahead1(); - if l.peek::() { - parser.parse::()?; - Ok(ValType::Funcref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::Anyref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::Nullref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::Exnref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::Eqref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::I31ref) - } else if l.peek::() { - parser.parse::()?; - Ok(ValType::Optref(parser.parse()?)) - } else if l.peek::() { - Ok(ValType::Ref(parser.parse()?)) - } else { - Err(l.error()) - } + Ok(ValType::Ref(p.parse()?)) } else if l.peek::() { p.parse::()?; - Ok(ValType::Optref(parser.parse()?)) + Ok(ValType::Ref(RefType::OptType(parser.parse()?))) } else if l.peek::() { p.parse::()?; Ok(ValType::Rtt(parser.parse()?)) @@ -104,13 +72,80 @@ impl<'a> Parse<'a> for ValType<'a> { }) } else if l.peek::() { parser.parse::()?; - Ok(ValType::Exnref) + Ok(ValType::Ref(RefType::Exn)) } else if l.peek::() { parser.parse::()?; - Ok(ValType::Eqref) + Ok(ValType::Ref(RefType::Eq)) } else if l.peek::() { parser.parse::()?; - Ok(ValType::I31ref) + Ok(ValType::Ref(RefType::I31)) + } else { + Err(l.error()) + } + } +} + +/// The reference value types for a wasm module. +#[allow(missing_docs)] +#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)] +pub enum RefType<'a> { + /// An untyped function reference: funcref. This is part of the reference + /// types proposal. + Func, + /// A reference to any host value: externref. This was originally known as + /// anyref when it was the supertype of all reference value types. This is + /// part of the reference types proposal. + Extern, + /// A reference to an exception: exnref. This is part of the exception + /// handling proposal. + Exn, + /// A reference that has an identity that can be compared: eqref. This is + /// part of the GC proposal. + Eq, + /// An unboxed 31-bit integer: i31ref. This may be going away if there is no common + /// supertype of all reference types. Part of the GC proposal. + I31, + /// A reference to a function, struct, or array: ref T. This is part of the + /// GC proposal. + Type(ast::Index<'a>), + /// A nullable reference to a function, struct, or array: optref T. This is + /// part of the GC proposal. + OptType(ast::Index<'a>), +} + +impl<'a> From for RefType<'a> { + fn from(elem: TableElemType) -> Self { + match elem { + TableElemType::Funcref => RefType::Func, + TableElemType::Externref => RefType::Extern, + TableElemType::Exnref => RefType::Exn, + } + } +} + +impl<'a> Parse<'a> for RefType<'a> { + fn parse(parser: Parser<'a>) -> Result { + let mut l = parser.lookahead1(); + if l.peek::() { + parser.parse::()?; + Ok(RefType::Func) + } else if l.peek::() { + parser.parse::()?; + Ok(RefType::Extern) + } else if l.peek::() { + parser.parse::()?; + Ok(RefType::Exn) + } else if l.peek::() { + parser.parse::()?; + Ok(RefType::Eq) + } else if l.peek::() { + parser.parse::()?; + Ok(RefType::I31) + } else if l.peek::() { + parser.parse::()?; + Ok(RefType::OptType(parser.parse()?)) + } else if l.peek::() { + Ok(RefType::Type(parser.parse()?)) } else { Err(l.error()) } @@ -146,16 +181,12 @@ impl<'a> Parse<'a> for GlobalType<'a> { } /// List of different kinds of table types we can have. -/// -/// Currently there's only one, a `funcref`. #[derive(Copy, Clone, Debug)] pub enum TableElemType { /// An element for a table that is a list of functions. Funcref, - /// An element for a table that is a list of `anyref` values. - Anyref, - /// An element for a table that is a list of `nullref` values. - Nullref, + /// An element for a table that is a list of `externref` values. + Externref, /// An element for a table that is a list of `exnref` values. Exnref, } @@ -172,11 +203,13 @@ impl<'a> Parse<'a> for TableElemType { parser.parse::()?; Ok(TableElemType::Funcref) } else if l.peek::() { + // Parse `anyref` as an alias of `externref` until all tests are + // ported to use the new name parser.parse::()?; - Ok(TableElemType::Anyref) - } else if l.peek::() { - parser.parse::()?; - Ok(TableElemType::Nullref) + Ok(TableElemType::Externref) + } else if l.peek::() { + parser.parse::()?; + Ok(TableElemType::Externref) } else if l.peek::() { parser.parse::()?; Ok(TableElemType::Exnref) @@ -190,7 +223,7 @@ impl Peek for TableElemType { fn peek(cursor: Cursor<'_>) -> bool { kw::funcref::peek(cursor) || kw::anyref::peek(cursor) - || kw::nullref::peek(cursor) + || kw::externref::peek(cursor) || /* legacy */ kw::anyfunc::peek(cursor) || kw::exnref::peek(cursor) } diff --git a/crates/wast/src/binary.rs b/crates/wast/src/binary.rs index e68890f..96add44 100644 --- a/crates/wast/src/binary.rs +++ b/crates/wast/src/binary.rs @@ -250,24 +250,33 @@ impl<'a> Encode for ValType<'a> { ValType::V128 => e.push(0x7b), ValType::I8 => e.push(0x7a), ValType::I16 => e.push(0x79), - ValType::Funcref => e.push(0x70), - ValType::Anyref => e.push(0x6f), - ValType::Nullref => e.push(0x6e), - ValType::Ref(index) => { - e.push(0x6d); + ValType::Ref(ty) => { + ty.encode(e); + } + ValType::Rtt(index) => { + e.push(0x69); index.encode(e); } - ValType::Optref(index) => { - e.push(0x6c); + } + } +} + +impl<'a> Encode for RefType<'a> { + fn encode(&self, e: &mut Vec) { + match self { + RefType::Func => e.push(0x70), + RefType::Extern => e.push(0x6f), + RefType::Eq => e.push(0x6b), + RefType::I31 => e.push(0x6a), + RefType::Exn => e.push(0x68), + RefType::Type(index) => { + e.push(0x6d); index.encode(e); } - ValType::Eqref => e.push(0x6b), - ValType::I31ref => e.push(0x6a), - ValType::Rtt(index) => { - e.push(0x69); + RefType::OptType(index) => { + e.push(0x6c); index.encode(e); } - ValType::Exnref => e.push(0x68), } } } @@ -329,10 +338,9 @@ impl Encode for TableType { impl Encode for TableElemType { fn encode(&self, e: &mut Vec) { match self { - TableElemType::Funcref => ValType::Funcref.encode(e), - TableElemType::Anyref => ValType::Anyref.encode(e), - TableElemType::Nullref => ValType::Nullref.encode(e), - TableElemType::Exnref => ValType::Exnref.encode(e), + TableElemType::Funcref => RefType::Func.encode(e), + TableElemType::Externref => RefType::Extern.encode(e), + TableElemType::Exnref => RefType::Exn.encode(e), } } } @@ -517,7 +525,7 @@ impl Encode for ElemPayload<'_> { fn encode(&self, e: &mut Vec) { match self { ElemPayload::Indices(v) => v.encode(e), - ElemPayload::Exprs { exprs, .. } => { + ElemPayload::Exprs { exprs, ty } => { exprs.len().encode(e); for idx in exprs { match idx { @@ -525,7 +533,7 @@ impl Encode for ElemPayload<'_> { Instruction::RefFunc(*idx).encode(e); } None => { - Instruction::RefNull.encode(e); + Instruction::RefNull((*ty).into()).encode(e); } } Instruction::End(None).encode(e); diff --git a/crates/wast/src/resolve/names.rs b/crates/wast/src/resolve/names.rs index e0619f7..19acef7 100644 --- a/crates/wast/src/resolve/names.rs +++ b/crates/wast/src/resolve/names.rs @@ -209,7 +209,20 @@ impl<'a> Resolver<'a> { fn resolve_valtype(&self, ty: &mut ValType<'a>) -> Result<(), Error> { match ty { - ValType::Ref(i) | ValType::Optref(i) | ValType::Rtt(i) => { + ValType::Ref(ty) => self.resolve_reftype(ty)?, + ValType::Rtt(i) => { + self.ns(Ns::Type) + .resolve(i) + .map_err(|id| self.resolve_error(id, "type"))?; + } + _ => {} + } + Ok(()) + } + + fn resolve_reftype(&self, ty: &mut RefType<'a>) -> Result<(), Error> { + match ty { + RefType::Type(i) | RefType::OptType(i) => { self.ns(Ns::Type) .resolve(i) .map_err(|id| self.resolve_error(id, "type"))?; @@ -548,6 +561,8 @@ impl<'a, 'b> ExprResolver<'a, 'b> { self.resolver.resolve_valtype(&mut s.to) } + RefNull(ty) | RefIsNull(ty) => self.resolver.resolve_reftype(ty), + _ => Ok(()), } } diff --git a/tests/regression/gc-struct.wat b/tests/regression/gc-struct.wat index 0c3bf43..9aec0cc 100644 --- a/tests/regression/gc-struct.wat +++ b/tests/regression/gc-struct.wat @@ -21,6 +21,5 @@ (func struct.narrow i32 f32 struct.narrow anyref funcref - struct.narrow anyref nullref ) ) diff --git a/tests/wabt.rs b/tests/wabt.rs index 75ea51c..c20f366 100644 --- a/tests/wabt.rs +++ b/tests/wabt.rs @@ -505,5 +505,23 @@ fn skip_test(test: &Path, contents: &str) -> bool { return true; } + // Waiting for wabt to remove subtyping from reference-types. + if test + .iter() + .any(|x| x == "bulk-memory-operations" || x == "reference-types") + || test.ends_with("reference-types.txt") + || test.ends_with("all-features.txt") + || test.ends_with("all-features.txt") + || test.ends_with("bulk-memory-named.txt") + || test.ends_with("reference-types-named.txt") + || test.ends_with("table-grow.txt") + || test.ends_with("result-exnref.txt") + || test.ends_with("global-exnref.txt") + || test.ends_with("global.txt") + || test.ends_with("bulk-memory.txt") + { + return true; + } + false }