Skip to content

Commit

Permalink
Update wast to align with GC spec tests (#1140)
Browse files Browse the repository at this point in the history
* Follow the latest shorthand for GC struct fields

(field) can no longer be omitted within structs, but (field) can now contain any number of types if you don't provide an identifier.

* Potentially support more wast return directives

* Allow folded expressions in element segment offsets (and items?)

* Cross my fingers and hope we never have anything after these refs

* Add ref.host for args and returns

* Make ref.extern argument optional in returns

* Fix doc link error

* Enable GC spec tests (needs submodule update)

Validation of GC spec tests has been suppressed in many cases. However, all the tests do parse successfully.

This commit does not include a necessary submodule update.

* Update test suite

* Run rustfmt

---------

Co-authored-by: Alex Crichton <alex@alexcrichton.com>
  • Loading branch information
bvisness and alexcrichton authored Sep 18, 2023
1 parent ea8d3f2 commit 1f38ce3
Show file tree
Hide file tree
Showing 156 changed files with 5,541 additions and 568 deletions.
10 changes: 5 additions & 5 deletions crates/wasmparser/src/readers/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1182,7 +1182,7 @@ impl<'a> TypeSectionReader<'a> {
RecGroup::Single(ty) => ty,
RecGroup::Many(_) => bail!(offset, "gc proposal not supported"),
};
if ty.is_final || ty.supertype_idx.is_some() {
if !ty.is_final || ty.supertype_idx.is_some() {
bail!(offset, "gc proposal not supported");
}
match ty.structural_type {
Expand Down Expand Up @@ -1216,7 +1216,7 @@ fn read_structural_type(
impl<'a> FromReader<'a> for RecGroup {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
Ok(match reader.peek()? {
0x4f => {
0x4e => {
reader.read_u8()?;
let types = reader.read_iter(MAX_WASM_TYPES, "rec group types")?;
RecGroup::Many(types.collect::<Result<_>>()?)
Expand All @@ -1230,7 +1230,7 @@ impl<'a> FromReader<'a> for SubType {
fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
let pos = reader.original_position();
Ok(match reader.read_u8()? {
opcode @ (0x4e | 0x50) => {
opcode @ (0x4f | 0x50) => {
let idx_iter = reader.read_iter(MAX_WASM_SUPERTYPES, "supertype idxs")?;
let idxs = idx_iter.collect::<Result<Vec<u32>>>()?;
if idxs.len() > 1 {
Expand All @@ -1240,13 +1240,13 @@ impl<'a> FromReader<'a> for SubType {
));
}
SubType {
is_final: opcode == 0x4e,
is_final: opcode == 0x4f,
supertype_idx: idxs.first().copied(),
structural_type: read_structural_type(reader.read_u8()?, reader)?,
}
}
opcode => SubType {
is_final: false,
is_final: true,
supertype_idx: None,
structural_type: read_structural_type(opcode, reader)?,
},
Expand Down
2 changes: 1 addition & 1 deletion crates/wasmparser/src/validator/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ impl Module {
types: &mut TypeAlloc,
offset: usize,
) -> Result<Type> {
if !features.gc && (ty.is_final || ty.supertype_idx.is_some()) {
if !features.gc && (!ty.is_final || ty.supertype_idx.is_some()) {
bail!(offset, "gc proposal must be enabled to use subtypes");
}

Expand Down
4 changes: 2 additions & 2 deletions crates/wasmprinter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ impl Printer {
self.print_func_type(states.last().unwrap(), &ty, None)?;
self.end_group();
Some(SubType {
is_final: false,
is_final: true,
supertype_idx: None,
structural_type: StructuralType::Func(ty),
})
Expand Down Expand Up @@ -683,7 +683,7 @@ impl Printer {
}

fn print_sub(&mut self, state: &State, ty: &SubType, names_for: Option<u32>) -> Result<u32> {
let r = if ty.is_final || !ty.supertype_idx.is_none() {
let r = if !ty.is_final || !ty.supertype_idx.is_none() {
self.start_group("sub");
self.print_sub_type(state, ty)?;
let r = self.print_structural(state, &ty.structural_type, names_for)?;
Expand Down
4 changes: 2 additions & 2 deletions crates/wast/src/core/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl Encode for Type<'_> {
match (&self.parent, self.final_type) {
(Some(parent), Some(true)) => {
// Type is final with a supertype
e.push(0x4e);
e.push(0x4f);
e.push(0x01);
parent.encode(e);
}
Expand Down Expand Up @@ -212,7 +212,7 @@ impl Encode for Type<'_> {

impl Encode for Rec<'_> {
fn encode(&self, e: &mut Vec<u8>) {
e.push(0x4f);
e.push(0x4e);
self.types.len().encode(e);
for ty in &self.types {
ty.encode(e);
Expand Down
50 changes: 50 additions & 0 deletions crates/wast/src/core/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,31 @@ impl<'a> Parse<'a> for Expression<'a> {
}
}

impl<'a> Expression<'a> {
/// Parse an expression formed from a single folded instruction.
///
/// Attempts to parse an expression formed from a single folded instruction.
///
/// This method will mutate the state of `parser` after attempting to parse
/// the expression. If an error happens then it is likely fatal and
/// there is no guarantee of how many tokens have been consumed from
/// `parser`.
///
/// # Errors
///
/// This function will return an error if the expression could not be
/// parsed. Note that creating an [`crate::Error`] is not exactly a cheap
/// operation, so [`crate::Error`] is typically fatal and propagated all the
/// way back to the top parse call site.
pub fn parse_folded_instruction(parser: Parser<'a>) -> Result<Self> {
let mut exprs = ExpressionParser::default();
exprs.parse_folded_instruction(parser)?;
Ok(Expression {
instrs: exprs.instrs.into(),
})
}
}

/// Helper struct used to parse an `Expression` with helper methods and such.
///
/// The primary purpose of this is to avoid defining expression parsing as a
Expand Down Expand Up @@ -217,6 +242,31 @@ impl<'a> ExpressionParser<'a> {
Ok(())
}

fn parse_folded_instruction(&mut self, parser: Parser<'a>) -> Result<()> {
let mut done = false;
while !done {
match self.paren(parser)? {
Paren::Left => {
self.stack.push(Level::EndWith(parser.parse()?));
}
Paren::Right => {
let top_instr = match self.stack.pop().unwrap() {
Level::EndWith(i) => i,
_ => panic!("unknown level type"),
};
self.instrs.push(top_instr);
if self.stack.is_empty() {
done = true;
}
}
Paren::None => {
return Err(parser.error("expected to continue a folded instruction"))
}
}
}
Ok(())
}

/// Parses either `(`, `)`, or nothing.
fn paren(&self, parser: Parser<'a>) -> Result<Paren> {
parser.step(|cursor| {
Expand Down
35 changes: 9 additions & 26 deletions crates/wast/src/core/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ impl<'a> Parse<'a> for Elem<'a> {
// * `(elem declare ...`
// * `(elem (table ...`
// * `(elem (offset ...`
// * `(elem (<instr> ...`
// * `(elem (<instr> ...` (omitted `offset`)
let mut table_omitted = false;
let kind = if parser.peek::<kw::declare>()? {
parser.parse::<kw::declare>()?;
Expand All @@ -186,20 +186,7 @@ impl<'a> Parse<'a> for Elem<'a> {
Index::Num(0, span)
};

// NB: this is technically not spec-compliant where the spec says
// that `(instr)` is equivalent to `(offset instr)` for
// single-element offsets. The test suite, however, has offsets of
// the form `(instr (instr-arg))` which doesn't seem to follow this.
// I don't know whether the test is correct or the spec is correct
// so we're left with this for now.
//
// In theory though this should use `parse_expr_or_single_instr`.
let offset = parser.parens(|parser| {
if parser.peek::<kw::offset>()? {
parser.parse::<kw::offset>()?;
}
parser.parse()
})?;
let offset = parse_expr_or_single_instr::<kw::offset>(parser)?;
ElemKind::Active { table, offset }
} else {
ElemKind::Passive
Expand Down Expand Up @@ -289,17 +276,13 @@ fn parse_expr_or_single_instr<'a, T>(parser: Parser<'a>) -> Result<Expression<'a
where
T: Parse<'a> + Peek,
{
parser.parens(|parser| {
if parser.peek::<T>()? {
if parser.peek2::<T>()? {
parser.parens(|parser| {
parser.parse::<T>()?;
parser.parse()
} else {
// Without `item` this is "sugar" for a single-instruction
// expression.
let insn = parser.parse()?;
Ok(Expression {
instrs: [insn].into(),
})
}
})
})
} else {
// Without `T` this is "sugar" for a single instruction (still possibly folded).
Ok(Expression::parse_folded_instruction(parser)?)
}
}
22 changes: 13 additions & 9 deletions crates/wast/src/core/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -596,15 +596,19 @@ impl<'a> Parse<'a> for StructType<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
let mut ret = StructType { fields: Vec::new() };
while !parser.is_empty() {
let field = if parser.peek2::<kw::field>()? {
parser.parens(|parser| {
parser.parse::<kw::field>()?;
StructField::parse(parser, true)
})
} else {
StructField::parse(parser, false)
};
ret.fields.push(field?);
parser.parens(|parser| {
parser.parse::<kw::field>()?;
if parser.peek::<Id>()? {
let field = StructField::parse(parser, true);
ret.fields.push(field?);
} else {
while !parser.is_empty() {
let field = StructField::parse(parser, false);
ret.fields.push(field?);
}
}
Ok(())
})?;
}
Ok(ret)
}
Expand Down
22 changes: 21 additions & 1 deletion crates/wast/src/core/wast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum WastArgCore<'a> {
V128(V128Const),
RefNull(HeapType<'a>),
RefExtern(u32),
RefHost(u32),
}

static ARGS: &[(&str, fn(Parser<'_>) -> Result<WastArgCore<'_>>)] = {
Expand All @@ -27,6 +28,7 @@ static ARGS: &[(&str, fn(Parser<'_>) -> Result<WastArgCore<'_>>)] = {
("v128.const", |p| Ok(V128(p.parse()?))),
("ref.null", |p| Ok(RefNull(p.parse()?))),
("ref.extern", |p| Ok(RefExtern(p.parse()?))),
("ref.host", |p| Ok(RefHost(p.parse()?))),
]
};

Expand Down Expand Up @@ -73,9 +75,21 @@ pub enum WastRetCore<'a> {
RefNull(Option<HeapType<'a>>),
/// A non-null externref is expected which should contain the specified
/// value.
RefExtern(u32),
RefExtern(Option<u32>),
/// A non-null anyref is expected which should contain the specified host value.
RefHost(u32),
/// A non-null funcref is expected.
RefFunc(Option<Index<'a>>),
/// A non-null anyref is expected.
RefAny,
/// A non-null eqref is expected.
RefEq,
/// A non-null arrayref is expected.
RefArray,
/// A non-null structref is expected.
RefStruct,
/// A non-null i31ref is expected.
RefI31,

Either(Vec<WastRetCore<'a>>),
}
Expand All @@ -90,7 +104,13 @@ static RETS: &[(&str, fn(Parser<'_>) -> Result<WastRetCore<'_>>)] = {
("v128.const", |p| Ok(V128(p.parse()?))),
("ref.null", |p| Ok(RefNull(p.parse()?))),
("ref.extern", |p| Ok(RefExtern(p.parse()?))),
("ref.host", |p| Ok(RefHost(p.parse()?))),
("ref.func", |p| Ok(RefFunc(p.parse()?))),
("ref.any", |_| Ok(RefAny)),
("ref.eq", |_| Ok(RefEq)),
("ref.array", |_| Ok(RefArray)),
("ref.struct", |_| Ok(RefStruct)),
("ref.i31", |_| Ok(RefI31)),
("either", |p| {
p.depth_check()?;
let mut cases = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion tests/cli/dump/alias.wat.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
| 01 00 00 00
0x57 | 01 04 | type section
0x59 | 01 | 1 count
0x5a | 60 00 00 | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x5a | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x5d | 03 02 | func section
0x5f | 01 | 1 count
0x60 | 00 | [func 0] type 0
Expand Down
4 changes: 2 additions & 2 deletions tests/cli/dump/alias2.wat.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@
| 01 00 00 00
0x158 | 01 04 | type section
0x15a | 01 | 1 count
0x15b | 60 00 00 | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x15b | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x15e | 03 02 | func section
0x160 | 01 | 1 count
0x161 | 00 | [func 0] type 0
Expand Down Expand Up @@ -162,7 +162,7 @@
| 01 00 00 00
0x1a2 | 01 04 | type section
0x1a4 | 01 | 1 count
0x1a5 | 60 00 00 | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x1a5 | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x1a8 | 02 19 | import section
0x1aa | 04 | 4 count
0x1ab | 00 01 31 00 | import [func 0] Import { module: "", name: "1", ty: Func(0) }
Expand Down
10 changes: 5 additions & 5 deletions tests/cli/dump/blockty.wat.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
| 01 00 00 00
0x8 | 01 17 | type section
0xa | 05 | 5 count
0xb | 60 00 00 | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0xe | 60 00 01 7f | [type 1] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [I32] }) })
0x12 | 60 01 7f 00 | [type 2] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) })
0x16 | 60 01 7f 01 | [type 3] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32] }) })
0xb | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0xe | 60 00 01 7f | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [I32] }) })
0x12 | 60 01 7f 00 | [type 2] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [] }) })
0x16 | 60 01 7f 01 | [type 3] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32] }) })
| 7f
0x1b | 60 01 7f 02 | [type 4] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32, I32] }) })
0x1b | 60 01 7f 02 | [type 4] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32], returns: [I32, I32] }) })
| 7f 7f
0x21 | 03 02 | func section
0x23 | 01 | 1 count
Expand Down
10 changes: 5 additions & 5 deletions tests/cli/dump/bundled.wat.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
| 01 00 00 00
0x54 | 01 09 | type section
0x56 | 01 | 1 count
0x57 | 60 04 7f 7f | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32, I32], returns: [I32] }) })
0x57 | 60 04 7f 7f | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32, I32], returns: [I32] }) })
| 7f 7f 01 7f
0x5f | 03 02 | func section
0x61 | 01 | 1 count
Expand Down Expand Up @@ -61,9 +61,9 @@
| 01 00 00 00
0xa0 | 01 09 | type section
0xa2 | 02 | 2 count
0xa3 | 60 02 7f 7f | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) })
0xa3 | 60 02 7f 7f | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) })
| 00
0xa8 | 60 00 00 | [type 1] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0xa8 | 60 00 00 | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
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) }
Expand Down Expand Up @@ -103,9 +103,9 @@
| 01 00 00 00
0x101 | 01 0c | type section
0x103 | 02 | 2 count
0x104 | 60 02 7f 7f | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) })
0x104 | 60 02 7f 7f | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32], returns: [] }) })
| 00
0x109 | 60 03 7f 7f | [type 1] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32], returns: [] }) })
0x109 | 60 03 7f 7f | [type 1] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [I32, I32, I32], returns: [] }) })
| 7f 00
0x10f | 02 12 | import section
0x111 | 01 | 1 count
Expand Down
4 changes: 2 additions & 2 deletions tests/cli/dump/component-expand-bundle.wat.stdout
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
| 01 00 00 00
0x12 | 01 04 | type section
0x14 | 01 | 1 count
0x15 | 60 00 00 | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x15 | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x18 | 03 02 | func section
0x1a | 01 | 1 count
0x1b | 00 | [func 0] type 0
Expand All @@ -28,7 +28,7 @@
| 01 00 00 00
0x3d | 01 04 | type section
0x3f | 01 | 1 count
0x40 | 60 00 00 | [type 0] Single(SubType { is_final: false, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x40 | 60 00 00 | [type 0] Single(SubType { is_final: true, supertype_idx: None, structural_type: Func(FuncType { params: [], returns: [] }) })
0x43 | 02 06 | import section
0x45 | 01 | 1 count
0x46 | 00 01 61 00 | import [func 0] Import { module: "", name: "a", ty: Func(0) }
Expand Down
Loading

0 comments on commit 1f38ce3

Please sign in to comment.