summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2022-12-08 11:50:45 -0600
committerGitHub <noreply@github.com>2022-12-08 09:50:45 -0800
commit48959ab5a74d849e9782f54b3535c6fca69d51d7 (patch)
treeb6da926b399636eb2cfc34ce6adbdc24b8488bcc
parent2cb5cefb6392619d908ce2ab683815d7e22ac9a5 (diff)
downloadbinaryen-48959ab5a74d849e9782f54b3535c6fca69d51d7.tar.gz
binaryen-48959ab5a74d849e9782f54b3535c6fca69d51d7.tar.bz2
binaryen-48959ab5a74d849e9782f54b3535c6fca69d51d7.zip
Allow casting to basic heap types (#5332)
The standard casting instructions now allow casting to basic heap types, not just user-defined types, but they also require that the intended type and argument type have a common supertype. Update the validator to use the standard rules, update the binary parser and printer to allow basic types, and update the tests to remove or modify newly invalid test cases.
-rw-r--r--src/wasm/wasm-binary.cpp11
-rw-r--r--src/wasm/wasm-stack.cpp6
-rw-r--r--src/wasm/wasm-validator.cpp61
-rw-r--r--test/lit/binary/legacy-static-casts.test1
-rw-r--r--test/lit/cast-to-basic.wast83
-rw-r--r--test/lit/passes/gufa-refs.wast24
-rw-r--r--test/lit/passes/optimize-instructions-gc-tnh.wast29
-rw-r--r--test/lit/passes/optimize-instructions-gc.wast50
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.txt25
-rw-r--r--test/passes/Oz_fuzz-exec_all-features.wast8
-rw-r--r--test/spec/ref_cast.wast59
11 files changed, 217 insertions, 140 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 7b2922d3c..996ee8cf6 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -6894,7 +6894,8 @@ bool WasmBinaryBuilder::maybeVisitI31Get(Expression*& out, uint32_t code) {
bool WasmBinaryBuilder::maybeVisitRefTest(Expression*& out, uint32_t code) {
if (code == BinaryConsts::RefTestStatic || code == BinaryConsts::RefTest) {
- auto intendedType = getIndexedHeapType();
+ bool legacy = code == BinaryConsts::RefTestStatic;
+ auto intendedType = legacy ? getIndexedHeapType() : getHeapType();
auto* ref = popNonVoidExpression();
out = Builder(wasm).makeRefTest(ref, intendedType);
return true;
@@ -6905,7 +6906,9 @@ bool WasmBinaryBuilder::maybeVisitRefTest(Expression*& out, uint32_t code) {
bool WasmBinaryBuilder::maybeVisitRefCast(Expression*& out, uint32_t code) {
if (code == BinaryConsts::RefCastStatic ||
code == BinaryConsts::RefCastNull || code == BinaryConsts::RefCastNop) {
- auto intendedType = getIndexedHeapType();
+ bool legacy =
+ code == BinaryConsts::RefCastStatic || code == BinaryConsts::RefCastNop;
+ auto intendedType = legacy ? getIndexedHeapType() : getHeapType();
auto* ref = popNonVoidExpression();
auto safety =
code == BinaryConsts::RefCastNop ? RefCast::Unsafe : RefCast::Safe;
@@ -6956,7 +6959,9 @@ bool WasmBinaryBuilder::maybeVisitBrOn(Expression*& out, uint32_t code) {
auto name = getBreakTarget(getU32LEB()).name;
HeapType intendedType;
if (op == BrOnCast || op == BrOnCastFail) {
- intendedType = getIndexedHeapType();
+ bool legacy = code == BinaryConsts::BrOnCastStatic ||
+ code == BinaryConsts::BrOnCastStaticFail;
+ intendedType = legacy ? getIndexedHeapType() : getHeapType();
}
auto* ref = popNonVoidExpression();
out = Builder(wasm).makeBrOn(op, name, ref, intendedType);
diff --git a/src/wasm/wasm-stack.cpp b/src/wasm/wasm-stack.cpp
index 1f477a10c..c4b9598c7 100644
--- a/src/wasm/wasm-stack.cpp
+++ b/src/wasm/wasm-stack.cpp
@@ -2026,7 +2026,7 @@ void BinaryInstWriter::visitCallRef(CallRef* curr) {
void BinaryInstWriter::visitRefTest(RefTest* curr) {
o << int8_t(BinaryConsts::GCPrefix);
o << U32LEB(BinaryConsts::RefTest);
- parent.writeIndexedHeapType(curr->intendedType);
+ parent.writeHeapType(curr->intendedType);
}
void BinaryInstWriter::visitRefCast(RefCast* curr) {
@@ -2036,7 +2036,7 @@ void BinaryInstWriter::visitRefCast(RefCast* curr) {
} else {
o << U32LEB(BinaryConsts::RefCastNull);
}
- parent.writeIndexedHeapType(curr->intendedType);
+ parent.writeHeapType(curr->intendedType);
}
void BinaryInstWriter::visitBrOn(BrOn* curr) {
@@ -2078,7 +2078,7 @@ void BinaryInstWriter::visitBrOn(BrOn* curr) {
}
o << U32LEB(getBreakIndex(curr->name));
if (curr->op == BrOnCast || curr->op == BrOnCastFail) {
- parent.writeIndexedHeapType(curr->intendedType);
+ parent.writeHeapType(curr->intendedType);
}
}
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index ba8712d35..16b6f5314 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -2504,49 +2504,54 @@ void FunctionValidator::visitI31Get(I31Get* curr) {
void FunctionValidator::visitRefTest(RefTest* curr) {
shouldBeTrue(
getModule()->features.hasGC(), curr, "ref.test requires gc [--enable-gc]");
- if (curr->ref->type != Type::unreachable) {
- shouldBeTrue(
- curr->ref->type.isRef(), curr, "ref.test ref must have ref type");
+ if (curr->ref->type == Type::unreachable) {
+ return;
}
- shouldBeUnequal(curr->intendedType,
- HeapType(),
- curr,
- "static ref.test must set intendedType field");
- shouldBeTrue(
- !curr->intendedType.isBasic(), curr, "ref.test must test a non-basic");
+ if (!shouldBeTrue(
+ curr->ref->type.isRef(), curr, "ref.test ref must have ref type")) {
+ return;
+ }
+ shouldBeEqual(
+ curr->intendedType.getBottom(),
+ curr->ref->type.getHeapType().getBottom(),
+ curr,
+ "ref.test target type and ref type must have a common supertype");
}
void FunctionValidator::visitRefCast(RefCast* curr) {
shouldBeTrue(
getModule()->features.hasGC(), curr, "ref.cast requires gc [--enable-gc]");
- if (curr->ref->type != Type::unreachable) {
- shouldBeTrue(
- curr->ref->type.isRef(), curr, "ref.cast ref must have ref type");
+ if (curr->ref->type == Type::unreachable) {
+ return;
}
- shouldBeUnequal(curr->intendedType,
- HeapType(),
- curr,
- "static ref.cast must set intendedType field");
- shouldBeTrue(
- !curr->intendedType.isBasic(), curr, "ref.cast must cast to a non-basic");
+ if (!shouldBeTrue(
+ curr->ref->type.isRef(), curr, "ref.cast ref must have ref type")) {
+ return;
+ }
+ shouldBeEqual(
+ curr->intendedType.getBottom(),
+ curr->ref->type.getHeapType().getBottom(),
+ curr,
+ "ref.cast target type and ref type must have a common supertype");
}
void FunctionValidator::visitBrOn(BrOn* curr) {
shouldBeTrue(getModule()->features.hasGC(),
curr,
"br_on_cast requires gc [--enable-gc]");
- if (curr->ref->type != Type::unreachable) {
- shouldBeTrue(
- curr->ref->type.isRef(), curr, "br_on_cast ref must have ref type");
+ if (curr->ref->type == Type::unreachable) {
+ return;
+ }
+ if (!shouldBeTrue(
+ curr->ref->type.isRef(), curr, "br_on_cast ref must have ref type")) {
+ return;
}
if (curr->op == BrOnCast || curr->op == BrOnCastFail) {
- shouldBeUnequal(curr->intendedType,
- HeapType(),
- curr,
- "static br_on_cast* must set intendedType field");
- shouldBeTrue(!curr->intendedType.isBasic(),
- curr,
- "br_on_cast* must cast to a non-basic");
+ shouldBeEqual(
+ curr->intendedType.getBottom(),
+ curr->ref->type.getHeapType().getBottom(),
+ curr,
+ "br_on_cast* target type and ref type must have a common supertype");
} else {
shouldBeEqual(curr->intendedType,
HeapType(),
diff --git a/test/lit/binary/legacy-static-casts.test b/test/lit/binary/legacy-static-casts.test
index 53218ba65..ad2209594 100644
--- a/test/lit/binary/legacy-static-casts.test
+++ b/test/lit/binary/legacy-static-casts.test
@@ -3,6 +3,7 @@
;; Test that the opcodes for the deprecated *_static cast instructions still parse.
;; RUN: wasm-opt %s.wasm -all -S -o - | filecheck %s
+
;; CHECK: (type ${} (struct ))
;; CHECK: (type $none_=>_none (func))
diff --git a/test/lit/cast-to-basic.wast b/test/lit/cast-to-basic.wast
new file mode 100644
index 000000000..c6b8ea9e7
--- /dev/null
+++ b/test/lit/cast-to-basic.wast
@@ -0,0 +1,83 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
+
+;; Test that casts to basic types round trip properly.
+
+;; RUN: wasm-opt %s -all --roundtrip -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (func $test (type $none_=>_i32) (result i32)
+ ;; CHECK-NEXT: (ref.test data
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $test (result i32)
+ (ref.test struct
+ (ref.null none)
+ )
+ )
+
+ ;; CHECK: (func $cast (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast null data
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (drop
+ (ref.cast null struct
+ (ref.null none)
+ )
+ )
+ )
+
+ ;; CHECK: (func $br (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $label$1 (result dataref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $label$1 data
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br
+ (drop
+ (block $l (result structref)
+ (drop
+ (br_on_cast $l struct
+ (ref.null none)
+ )
+ )
+ (ref.null none)
+ )
+ )
+ )
+
+ ;; CHECK: (func $br-fail (type $none_=>_none)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $label$1 (result dataref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $label$1 data
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-fail
+ (drop
+ (block $l (result structref)
+ (drop
+ (br_on_cast_fail $l struct
+ (ref.null none)
+ )
+ )
+ (ref.null none)
+ )
+ )
+ )
+)
diff --git a/test/lit/passes/gufa-refs.wast b/test/lit/passes/gufa-refs.wast
index f209cef88..2b221dfd8 100644
--- a/test/lit/passes/gufa-refs.wast
+++ b/test/lit/passes/gufa-refs.wast
@@ -981,7 +981,8 @@
;; CHECK: (type $none_=>_none (func))
- ;; CHECK: (elem declare func $func)
+ ;; CHECK: (type $unrelated (struct ))
+ (type $unrelated (struct))
;; CHECK: (func $func (type $none_=>_none)
;; CHECK-NEXT: (local $child (ref null $child))
@@ -1034,13 +1035,8 @@
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (block $parent (result (ref $parent))
;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $none_=>_none))
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (br_on_cast $parent $parent
- ;; CHECK-NEXT: (ref.func $func)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (ref.func $func)
+ ;; CHECK-NEXT: (br_on_cast $parent $parent
+ ;; CHECK-NEXT: (struct.new_default $unrelated)
;; CHECK-NEXT: )
;; CHECK-NEXT: )
;; CHECK-NEXT: (unreachable)
@@ -1087,14 +1083,14 @@
(local.get $parent)
)
)
- ;; A ref.func is cast to a struct type, and then we read from that. The cast
- ;; will trap at runtime, of course; for here, we should not error and also
- ;; we can optimize these to unreachables. atm we filter out trapping
- ;; contents in ref.cast, but not br_on_cast, so test both.
+ ;; An unrelated type is cast to a struct type, and then we read from that.
+ ;; The cast will trap at runtime, of course; for here, we should not error
+ ;; and also we can optimize these to unreachables. atm we filter out
+ ;; trapping contents in ref.cast, but not br_on_cast, so test both.
(drop
(struct.get $parent 0
(ref.cast null $parent
- (ref.func $func)
+ (struct.new $unrelated)
)
)
)
@@ -1103,7 +1099,7 @@
(block $parent (result (ref $parent))
(drop
(br_on_cast $parent $parent
- (ref.func $func)
+ (struct.new $unrelated)
)
)
(unreachable)
diff --git a/test/lit/passes/optimize-instructions-gc-tnh.wast b/test/lit/passes/optimize-instructions-gc-tnh.wast
index 2d3e25b9b..741cd3921 100644
--- a/test/lit/passes/optimize-instructions-gc-tnh.wast
+++ b/test/lit/passes/optimize-instructions-gc-tnh.wast
@@ -48,43 +48,34 @@
)
)
- ;; TNH: (func $ref.eq-no (type $eqref_eqref_=>_none) (param $a eqref) (param $b eqref)
+ ;; TNH: (func $ref.eq-no (type $eqref_eqref_anyref_=>_none) (param $a eqref) (param $b eqref) (param $any anyref)
;; TNH-NEXT: (drop
;; TNH-NEXT: (i32.const 1)
;; TNH-NEXT: )
;; TNH-NEXT: )
- ;; NO_TNH: (func $ref.eq-no (type $eqref_eqref_=>_none) (param $a eqref) (param $b eqref)
+ ;; NO_TNH: (func $ref.eq-no (type $eqref_eqref_anyref_=>_none) (param $a eqref) (param $b eqref) (param $any anyref)
;; NO_TNH-NEXT: (drop
;; NO_TNH-NEXT: (ref.eq
- ;; NO_TNH-NEXT: (block (result (ref $struct))
- ;; NO_TNH-NEXT: (drop
- ;; NO_TNH-NEXT: (ref.func $ref.eq-no)
- ;; NO_TNH-NEXT: )
- ;; NO_TNH-NEXT: (unreachable)
+ ;; NO_TNH-NEXT: (ref.cast null $struct
+ ;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
- ;; NO_TNH-NEXT: (block (result (ref data))
- ;; NO_TNH-NEXT: (drop
- ;; NO_TNH-NEXT: (ref.func $ref.eq-no)
- ;; NO_TNH-NEXT: )
- ;; NO_TNH-NEXT: (unreachable)
+ ;; NO_TNH-NEXT: (ref.as_data
+ ;; NO_TNH-NEXT: (local.get $any)
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
;; NO_TNH-NEXT: )
- (func $ref.eq-no (param $a (ref null eq)) (param $b (ref null eq))
- ;; We must leave the inputs to ref.eq of type eqref or a subtype. Note that
- ;; these casts will trap, so other opts might get in the way before we can
- ;; do anything. The crucial thing we test here is that we do not emit
- ;; something that does not validate (as ref.eq inputs must be eqrefs).
+ (func $ref.eq-no (param $a (ref null eq)) (param $b (ref null eq)) (param $any anyref)
+ ;; We must leave the inputs to ref.eq of type eqref or a subtype.
(drop
(ref.eq
(ref.cast null $struct
- (ref.func $ref.eq-no) ;; *Not* an eqref!
+ (local.get $any) ;; *Not* an eqref!
)
(ref.as_non_null
(ref.as_data
(ref.as_non_null
- (ref.func $ref.eq-no) ;; *Not* an eqref!
+ (local.get $any) ;; *Not* an eqref!
)
)
)
diff --git a/test/lit/passes/optimize-instructions-gc.wast b/test/lit/passes/optimize-instructions-gc.wast
index 61b558868..d0ab6f859 100644
--- a/test/lit/passes/optimize-instructions-gc.wast
+++ b/test/lit/passes/optimize-instructions-gc.wast
@@ -1000,13 +1000,6 @@
;; CHECK-NEXT: )
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (ref.cast null $struct
- ;; CHECK-NEXT: (ref.as_func
- ;; CHECK-NEXT: (local.get $x)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (ref.cast null $struct
;; CHECK-NEXT: (ref.as_i31
;; CHECK-NEXT: (local.get $x)
;; CHECK-NEXT: )
@@ -1030,13 +1023,6 @@
;; NOMNL-NEXT: )
;; NOMNL-NEXT: (drop
;; NOMNL-NEXT: (ref.cast null $struct
- ;; NOMNL-NEXT: (ref.as_func
- ;; NOMNL-NEXT: (local.get $x)
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (ref.cast null $struct
;; NOMNL-NEXT: (ref.as_i31
;; NOMNL-NEXT: (local.get $x)
;; NOMNL-NEXT: )
@@ -1066,13 +1052,6 @@
;; other ref.as* operations are ignored for now
(drop
(ref.cast null $struct
- (ref.as_func
- (local.get $x)
- )
- )
- )
- (drop
- (ref.cast null $struct
(ref.as_i31
(local.get $x)
)
@@ -2130,35 +2109,6 @@
)
)
- ;; CHECK: (func $ref-cast-static-impossible (type $ref|func|_=>_none) (param $func (ref func))
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (block (result (ref $struct))
- ;; CHECK-NEXT: (drop
- ;; CHECK-NEXT: (local.get $func)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: (unreachable)
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; CHECK-NEXT: )
- ;; NOMNL: (func $ref-cast-static-impossible (type $ref|func|_=>_none) (param $func (ref func))
- ;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (block (result (ref $struct))
- ;; NOMNL-NEXT: (drop
- ;; NOMNL-NEXT: (local.get $func)
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: (unreachable)
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: )
- ;; NOMNL-NEXT: )
- (func $ref-cast-static-impossible (param $func (ref func))
- ;; A func cannot be cast to a struct, so this will trap.
- (drop
- (ref.cast null $struct
- (local.get $func)
- )
- )
- )
-
;; CHECK: (func $ref-cast-static-general (type $ref?|$A|_ref?|$B|_=>_none) (param $a (ref null $A)) (param $b (ref null $B))
;; CHECK-NEXT: (drop
;; CHECK-NEXT: (local.get $a)
diff --git a/test/passes/Oz_fuzz-exec_all-features.txt b/test/passes/Oz_fuzz-exec_all-features.txt
index afeae72f0..af706cc23 100644
--- a/test/passes/Oz_fuzz-exec_all-features.txt
+++ b/test/passes/Oz_fuzz-exec_all-features.txt
@@ -34,8 +34,6 @@
[host limit allocation failure]
[fuzz-exec] calling init-array-packed
[fuzz-exec] note result: init-array-packed => 213
-[fuzz-exec] calling cast-func-to-struct
-[trap cast error]
[fuzz-exec] calling array-copy
[LoggingExternalInterface logging 10]
[LoggingExternalInterface logging 10]
@@ -80,13 +78,12 @@
(export "cast-on-func" (func $12))
(export "array-alloc-failure" (func $7))
(export "init-array-packed" (func $14))
- (export "cast-func-to-struct" (func $9))
- (export "array-copy" (func $17))
- (export "array.init_static" (func $18))
- (export "array.init_static-packed" (func $19))
- (export "static-casts" (func $20))
+ (export "array-copy" (func $16))
+ (export "array.init_static" (func $17))
+ (export "array.init_static-packed" (func $18))
+ (export "static-casts" (func $19))
(export "static-br_on_cast" (func $2))
- (export "static-br_on_cast_fail" (func $22))
+ (export "static-br_on_cast_fail" (func $21))
(func $0 (type $void_func) (; has Stack IR ;)
(local $0 i32)
(call $log
@@ -225,7 +222,7 @@
(i32.const 10)
)
)
- (func $17 (type $void_func) (; has Stack IR ;)
+ (func $16 (type $void_func) (; has Stack IR ;)
(local $0 (ref $bytes))
(local $1 (ref $bytes))
(array.set $bytes
@@ -280,7 +277,7 @@
)
)
)
- (func $18 (type $void_func) (; has Stack IR ;)
+ (func $17 (type $void_func) (; has Stack IR ;)
(local $0 (ref $bytes))
(call $log
(array.len
@@ -305,7 +302,7 @@
)
)
)
- (func $19 (type $void_func) (; has Stack IR ;)
+ (func $18 (type $void_func) (; has Stack IR ;)
(call $log
(array.get_u $bytes
(array.init_static $bytes
@@ -315,7 +312,7 @@
)
)
)
- (func $20 (type $void_func) (; has Stack IR ;)
+ (func $19 (type $void_func) (; has Stack IR ;)
(call $log
(i32.const 1)
)
@@ -335,7 +332,7 @@
(i32.const 0)
)
)
- (func $22 (type $void_func) (; has Stack IR ;)
+ (func $21 (type $void_func) (; has Stack IR ;)
(call $log
(i32.const -2)
)
@@ -376,8 +373,6 @@
[fuzz-exec] calling array-alloc-failure
[fuzz-exec] calling init-array-packed
[fuzz-exec] note result: init-array-packed => 213
-[fuzz-exec] calling cast-func-to-struct
-[trap unreachable]
[fuzz-exec] calling array-copy
[LoggingExternalInterface logging 10]
[LoggingExternalInterface logging 10]
diff --git a/test/passes/Oz_fuzz-exec_all-features.wast b/test/passes/Oz_fuzz-exec_all-features.wast
index 203fc60fe..4c6af8a93 100644
--- a/test/passes/Oz_fuzz-exec_all-features.wast
+++ b/test/passes/Oz_fuzz-exec_all-features.wast
@@ -233,14 +233,6 @@
(func $call-target (param $0 eqref)
(nop)
)
- (func "cast-func-to-struct"
- (drop
- ;; An impossible cast of a function to a struct, which should fail.
- (ref.cast null $struct
- (ref.func $call-target)
- )
- )
- )
(func "array-copy"
(local $x (ref null $bytes))
(local $y (ref null $bytes))
diff --git a/test/spec/ref_cast.wast b/test/spec/ref_cast.wast
index 53ca9227c..22859d167 100644
--- a/test/spec/ref_cast.wast
+++ b/test/spec/ref_cast.wast
@@ -61,7 +61,66 @@
(drop (ref.cast null $t2 (global.get $tab.12)))
)
+
+ (func (export "test-ref-test-t0") (result i32)
+ (ref.test $t0 (struct.new $t0))
+ )
+
+ (func (export "test-ref-test-struct") (result i32)
+ (ref.test struct (struct.new $t0))
+ )
+
+ (func (export "test-ref-test-any") (result i32)
+ (ref.test any (struct.new $t0))
+ )
+
+ (func (export "test-ref-cast-struct")
+ (drop
+ (ref.cast null struct (struct.new $t0))
+ )
+ )
+
+ (func (export "test-br-on-cast-struct") (result i32)
+ (drop
+ (block $l (result (ref struct))
+ (drop
+ (br_on_cast $l struct (struct.new $t0))
+ )
+ (return (i32.const 0))
+ )
+ )
+ (i32.const 1)
+ )
+
+ (func (export "test-br-on-cast-fail-struct") (result i32)
+ (drop
+ (block $l (result (ref struct))
+ (drop
+ (br_on_cast_fail $l struct (struct.new $t0))
+ )
+ (return (i32.const 0))
+ )
+ )
+ (i32.const 1)
+ )
)
+
(invoke "test-sub")
(invoke "test-canon")
+(assert_return (invoke "test-ref-test-t0") (i32.const 1))
+(assert_return (invoke "test-ref-test-struct") (i32.const 1))
+(assert_return (invoke "test-ref-test-any") (i32.const 1))
+(assert_return (invoke "test-ref-cast-struct"))
+(assert_return (invoke "test-br-on-cast-struct") (i32.const 1))
+(assert_return (invoke "test-br-on-cast-fail-struct") (i32.const 0))
+
+(assert_invalid
+ (module
+ (type $t0 (struct))
+ (func (export "test-ref-test-extern") (result i32)
+ (ref.test extern (struct.new $t0))
+ )
+ )
+ "common supertype"
+)