summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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"
+)