summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorAlon Zakai <azakai@google.com>2023-02-03 10:25:02 -0800
committerGitHub <noreply@github.com>2023-02-03 10:25:02 -0800
commit42b859f01df94dfffb01258b2305df8ec4d05304 (patch)
tree3caaa872d4d36b7840025f4a4cf1654026ed5f4b /test
parentd2d5135b14c2b36cdd6b5a5d00a7734fe385fc37 (diff)
downloadbinaryen-42b859f01df94dfffb01258b2305df8ec4d05304.tar.gz
binaryen-42b859f01df94dfffb01258b2305df8ec4d05304.tar.bz2
binaryen-42b859f01df94dfffb01258b2305df8ec4d05304.zip
[Wasm GC] Add AbstractTypeRefining pass (#5461)
If a type hierarchy has abstract classes in the middle, that is, types that are never instantiated, then we can optimize casts and other operations to them. Say in Java that we have `AbstractList`, and it only has one subclass `IntList` that is ever created, then any place we have an `AbstractList` we must actually have an `IntList`, or a null. (Or, if no subtype is instantiated, then the value must definitely be a null.) The actual implementation does a type mapping, that is, it finds all places using an abstract type and makes them refer to the single instantiated subtype (or null). After that change, no references to the abstract type remain in the program, so this both refines types and also cleans up the type section.
Diffstat (limited to 'test')
-rw-r--r--test/lit/help/wasm-opt.test3
-rw-r--r--test/lit/help/wasm2js.test3
-rw-r--r--test/lit/passes/abstract-type-refining.wast1293
3 files changed, 1299 insertions, 0 deletions
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index 186382fd4..351f2c93e 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -83,6 +83,9 @@
;; CHECK-NEXT: Optimization passes:
;; CHECK-NEXT: --------------------
;; CHECK-NEXT:
+;; CHECK-NEXT: --abstract-type-refining refine and merge abstract
+;; CHECK-NEXT: (never-created) types
+;; CHECK-NEXT:
;; CHECK-NEXT: --alignment-lowering lower unaligned loads and stores
;; CHECK-NEXT: to smaller aligned ones
;; CHECK-NEXT:
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index 501504027..502419cf2 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -42,6 +42,9 @@
;; CHECK-NEXT: Optimization passes:
;; CHECK-NEXT: --------------------
;; CHECK-NEXT:
+;; CHECK-NEXT: --abstract-type-refining refine and merge abstract
+;; CHECK-NEXT: (never-created) types
+;; CHECK-NEXT:
;; CHECK-NEXT: --alignment-lowering lower unaligned loads and stores
;; CHECK-NEXT: to smaller aligned ones
;; CHECK-NEXT:
diff --git a/test/lit/passes/abstract-type-refining.wast b/test/lit/passes/abstract-type-refining.wast
new file mode 100644
index 000000000..f373acd87
--- /dev/null
+++ b/test/lit/passes/abstract-type-refining.wast
@@ -0,0 +1,1293 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt --abstract-type-refining --traps-never-happen -all --closed-world --nominal -S -o - | filecheck %s --check-prefix=YESTNH
+;; RUN: foreach %s %t wasm-opt --abstract-type-refining -all --closed-world --nominal -S -o - | filecheck %s --check-prefix=NO_TNH
+
+;; Run in both TNH and non-TNH mode.
+
+;; $A :> $B :> $C :> $D :> $E
+;;
+;; $A and $D have no struct.news, so any operations on them must, in TNH mode,
+;; actually refer to a subtype of them (that has a struct.new). As a result, in
+;; TNH mode $A and $D will also not be emitted in the output anymore.
+(module
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (type $A (struct ))
+ (type $A (struct))
+
+ ;; YESTNH: (type $B (struct ))
+ ;; NO_TNH: (type $B (struct_subtype $A))
+ (type $B (struct_subtype $A))
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (type $C (struct_subtype $B))
+ ;; NO_TNH: (type $C (struct_subtype $B))
+ (type $C (struct_subtype $B))
+
+ ;; NO_TNH: (type $D (struct_subtype $C))
+ (type $D (struct_subtype $C))
+
+ ;; YESTNH: (type $E (struct_subtype $C))
+ ;; NO_TNH: (type $E (struct_subtype $D))
+ (type $E (struct_subtype $D))
+
+ ;; YESTNH: (type $none_=>_none (func))
+
+ ;; YESTNH: (global $global anyref (struct.new_default $B))
+ ;; NO_TNH: (type $none_=>_none (func))
+
+ ;; NO_TNH: (global $global anyref (struct.new_default $B))
+ (global $global anyref (struct.new $B))
+
+ ;; YESTNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (struct.new_default $C)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (struct.new_default $E)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (struct.new_default $C)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (struct.new_default $E)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $new (param $x anyref)
+ (drop
+ (struct.new $C)
+ )
+ (drop
+ (struct.new $E)
+ )
+ )
+
+ ;; YESTNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $E
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $E
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $C
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $D
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $E
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast (param $x anyref)
+ ;; List out all possible casts for comprehensiveness. For other instructions
+ ;; we are more focused, below.
+ (drop
+ (ref.cast $A ;; This will be $B in TNH.
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $D ;; This will be $E in TNH.
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $E
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $ref.test (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.test $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.test (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.test $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.test (param $x anyref)
+ (drop
+ (ref.test $A
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $br_on (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (block $block (result (ref $B))
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (br_on_cast $block $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (unreachable)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $br_on (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (block $block (result anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (br_on_cast $block $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (unreachable)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $br_on (param $x anyref)
+ (drop
+ (block $block (result anyref)
+ (drop
+ (br_on_cast $block $A
+ (local.get $x)
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $basic (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast struct
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.as_i31
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $basic (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast struct
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.as_i31
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $basic (param $x anyref)
+ ;; Casts to basic types should not be modified.
+ (drop
+ (ref.cast struct
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.as_i31
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $locals (type $none_=>_none)
+ ;; YESTNH-NEXT: (local $A (ref $B))
+ ;; YESTNH-NEXT: (local $B (ref $B))
+ ;; YESTNH-NEXT: (local $C (ref $C))
+ ;; YESTNH-NEXT: (local $D (ref $E))
+ ;; YESTNH-NEXT: (local $E (ref $E))
+ ;; YESTNH-NEXT: (nop)
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $locals (type $none_=>_none)
+ ;; NO_TNH-NEXT: (local $A (ref $A))
+ ;; NO_TNH-NEXT: (local $B (ref $B))
+ ;; NO_TNH-NEXT: (local $C (ref $C))
+ ;; NO_TNH-NEXT: (local $D (ref $D))
+ ;; NO_TNH-NEXT: (local $E (ref $E))
+ ;; NO_TNH-NEXT: (nop)
+ ;; NO_TNH-NEXT: )
+ (func $locals
+ ;; Local variable types are also updated.
+ (local $A (ref $A))
+ (local $B (ref $B))
+ (local $C (ref $C))
+ (local $D (ref $D))
+ (local $E (ref $E))
+ )
+)
+
+;; $A has two subtypes. As a result, we cannot optimize it.
+(module
+ ;; YESTNH: (type $A (struct ))
+ ;; NO_TNH: (type $A (struct ))
+ (type $A (struct))
+
+ ;; YESTNH: (type $B (struct_subtype $A))
+ ;; NO_TNH: (type $B (struct_subtype $A))
+ (type $B (struct_subtype $A))
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (type $B1 (struct_subtype $A))
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (type $B1 (struct_subtype $A))
+ (type $B1 (struct_subtype $A)) ;; this is a new type
+
+ ;; YESTNH: (global $global anyref (struct.new_default $B))
+ ;; NO_TNH: (global $global anyref (struct.new_default $B))
+ (global $global anyref (struct.new $B))
+
+ ;; YESTNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (struct.new_default $B1)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (struct.new_default $B1)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $new (param $x anyref)
+ (drop
+ (struct.new $B1)
+ )
+ )
+
+ ;; YESTNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $A
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B1
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast (param $x anyref)
+ (drop
+ (ref.cast $A ;; This will not be optimized like before.
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B1
+ (local.get $x)
+ )
+ )
+ )
+)
+
+;; As above, but now $B is never created, so we can optimize casts of $A to
+;; $B1.
+(module
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (type $A (struct ))
+ (type $A (struct))
+
+ (type $B (struct_subtype $A))
+
+ ;; YESTNH: (type $B1 (struct ))
+ ;; NO_TNH: (type $B1 (struct_subtype $A))
+ (type $B1 (struct_subtype $A)) ;; this is a new type
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (struct.new_default $B1)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (struct.new_default $B1)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $new (param $x anyref)
+ (drop
+ (struct.new $B1)
+ )
+ )
+
+ ;; YESTNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B1
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast (param $x anyref)
+ (drop
+ (ref.cast $A ;; This will be optimized to $B1.
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B ;; $B is never created, so this will trap, in both TNH
+ (local.get $x) ;; and non-TNH modes.
+ )
+ )
+ (drop
+ (ref.cast $B1
+ (local.get $x)
+ )
+ )
+ )
+)
+
+;; A chain, $A :> $B :> $C, where we can optimize $A all the way to $C.
+(module
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (type $A (struct ))
+ (type $A (struct))
+
+ ;; NO_TNH: (type $B (struct_subtype $A))
+ (type $B (struct_subtype $A))
+
+ ;; YESTNH: (type $C (struct ))
+ ;; NO_TNH: (type $C (struct_subtype $B))
+ (type $C (struct_subtype $B))
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (struct.new_default $C)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $new (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (struct.new_default $C)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $new (param $x anyref)
+ (drop
+ (struct.new $C)
+ )
+ )
+
+ ;; YESTNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $C
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast (param $x anyref)
+ (drop
+ (ref.cast $A ;; This can be $C.
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B ;; This can also be $C.
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C
+ (local.get $x)
+ )
+ )
+ )
+)
+
+;; More testing for cases where no types or subtypes are created. No type is
+;; created here. No type needs to be emitted in the output.
+(module
+ (type $A (struct))
+
+ (type $B (struct_subtype $A))
+
+ (type $C1 (struct_subtype $B))
+
+ (type $C2 (struct_subtype $B))
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (type $none_=>_none (func))
+
+ ;; YESTNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (type $none_=>_none (func))
+
+ ;; NO_TNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast (param $x anyref)
+ ;; All these will trap.
+ (drop
+ (ref.cast $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C1
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C2
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $ref.cast.null (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast.null (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast.null (param $x anyref)
+ ;; These can only pass through a null.
+ (drop
+ (ref.cast null $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast null $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast null $C1
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast null $C2
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $ref.test (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.test none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.test null none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.test (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.test none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.test null none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.test (param $x anyref)
+ ;; This will return 0.
+ (drop
+ (ref.test $A
+ (local.get $x)
+ )
+ )
+ ;; This can test for a null.
+ (drop
+ (ref.test null $A
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $br_on (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (block $block (result (ref none))
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (br_on_cast $block none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (unreachable)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (block $block0 (result (ref any))
+ ;; YESTNH-NEXT: (br_on_non_null $block0
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (unreachable)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $br_on (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (block $block (result (ref none))
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (br_on_cast $block none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (unreachable)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (block $block0 (result (ref any))
+ ;; NO_TNH-NEXT: (br_on_non_null $block0
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (unreachable)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $br_on (param $x anyref)
+ ;; As above, this can be a cast to the bottom type.
+ (drop
+ (block $block (result anyref)
+ (drop
+ (br_on_cast $block $B
+ (local.get $x)
+ )
+ )
+ (unreachable)
+ )
+ )
+ ;; Non-cast br_on* can be ignored.
+ (drop
+ (block $block (result anyref)
+ (br_on_non_null $block
+ (local.get $x)
+ )
+ (unreachable)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $locals (type $none_=>_none)
+ ;; YESTNH-NEXT: (local $A (ref none))
+ ;; YESTNH-NEXT: (local $B (ref none))
+ ;; YESTNH-NEXT: (local $C1 (ref none))
+ ;; YESTNH-NEXT: (local $C2 nullref)
+ ;; YESTNH-NEXT: (nop)
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $locals (type $none_=>_none)
+ ;; NO_TNH-NEXT: (local $A (ref none))
+ ;; NO_TNH-NEXT: (local $B (ref none))
+ ;; NO_TNH-NEXT: (local $C1 (ref none))
+ ;; NO_TNH-NEXT: (local $C2 nullref)
+ ;; NO_TNH-NEXT: (nop)
+ ;; NO_TNH-NEXT: )
+ (func $locals
+ ;; All these locals can become nullable or even non-nullable null types.
+ ;; This checks no problem happens due to that.
+ (local $A (ref $A))
+ (local $B (ref $B))
+ (local $C1 (ref $C1))
+ (local $C2 (ref null $C2))
+ )
+)
+
+;; As above, but now $C1 is created.
+(module
+ ;; NO_TNH: (type $A (struct ))
+ (type $A (struct))
+
+ ;; NO_TNH: (type $B (struct_subtype $A))
+ (type $B (struct_subtype $A))
+
+ ;; YESTNH: (type $C1 (struct ))
+ ;; NO_TNH: (type $C1 (struct_subtype $B))
+ (type $C1 (struct_subtype $B))
+
+ (type $C2 (struct_subtype $B))
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (global $global anyref (struct.new_default $C1))
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (global $global anyref (struct.new_default $C1))
+ (global $global anyref (struct.new $C1))
+
+ ;; YESTNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $C1
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast (param $x anyref)
+ ;; These three can be cast to $C1 in TNH.
+ (drop
+ (ref.cast $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C1
+ (local.get $x)
+ )
+ )
+ ;; This will trap.
+ (drop
+ (ref.cast $C2
+ (local.get $x)
+ )
+ )
+ )
+
+ ;; YESTNH: (func $ref.cast.null (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null $C1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null $C1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null $C1
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast null none
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $ref.cast.null (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null $C1
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast null none
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $ref.cast.null (param $x anyref)
+ ;; These three can be cast to $C1 in TNH.
+ (drop
+ (ref.cast null $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast null $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast null $C1
+ (local.get $x)
+ )
+ )
+ ;; This returns null.
+ (drop
+ (ref.cast null $C2
+ (local.get $x)
+ )
+ )
+ )
+)
+
+;; Function subtyping, which is a TODO - for now we do nothing.
+(module
+ ;; YESTNH: (type $A (func))
+ ;; NO_TNH: (type $A (func))
+ (type $A (func))
+
+ ;; YESTNH: (type $funcref_=>_none (func (param funcref)))
+
+ ;; YESTNH: (type $B (func_subtype $A))
+ ;; NO_TNH: (type $funcref_=>_none (func (param funcref)))
+
+ ;; NO_TNH: (type $B (func_subtype $A))
+ (type $B (func_subtype $A))
+
+ ;; YESTNH: (type $C (func_subtype $B))
+ ;; NO_TNH: (type $C (func_subtype $B))
+ (type $C (func_subtype $B))
+
+ ;; YESTNH: (func $A (type $A)
+ ;; YESTNH-NEXT: (nop)
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $A (type $A)
+ ;; NO_TNH-NEXT: (nop)
+ ;; NO_TNH-NEXT: )
+ (func $A (type $A)
+ )
+
+ ;; YESTNH: (func $C (type $A)
+ ;; YESTNH-NEXT: (nop)
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $C (type $A)
+ ;; NO_TNH-NEXT: (nop)
+ ;; NO_TNH-NEXT: )
+ (func $C (type $A)
+ )
+
+ ;; YESTNH: (func $casts (type $funcref_=>_none) (param $x funcref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $A
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $casts (type $funcref_=>_none) (param $x funcref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $C
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $casts (param $x funcref)
+ ;; $A and $C have functions of their types, so in theory we could optimize
+ ;; $B here.
+ (drop
+ (ref.cast $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C
+ (local.get $x)
+ )
+ )
+ )
+)
+
+;; As above, but now the functions are also public types (exported). We should
+;; be careful here in the future even when we do optimize function types.
+(module
+ ;; YESTNH: (type $A (func))
+ ;; NO_TNH: (type $A (func))
+ (type $A (func))
+
+ ;; YESTNH: (type $B (func_subtype $A))
+ ;; NO_TNH: (type $B (func_subtype $A))
+ (type $B (func_subtype $A))
+
+ ;; YESTNH: (type $C (func_subtype $B))
+ ;; NO_TNH: (type $C (func_subtype $B))
+ (type $C (func_subtype $B))
+
+ ;; YESTNH: (type $funcref_=>_none (func (param funcref)))
+
+ ;; YESTNH: (elem declare func $A $C)
+
+ ;; YESTNH: (export "A" (func $A))
+
+ ;; YESTNH: (func $A (type $A)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.func $A)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (type $funcref_=>_none (func (param funcref)))
+
+ ;; NO_TNH: (elem declare func $A $C)
+
+ ;; NO_TNH: (export "A" (func $A))
+
+ ;; NO_TNH: (func $A (type $A)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.func $A)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $A (export "A") (type $A)
+ ;; Also create a function reference to use the type in that way as well.
+ (drop
+ (ref.func $A)
+ )
+ )
+
+ ;; YESTNH: (func $C (type $C)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.func $C)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $C (type $C)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.func $C)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $C (type $C)
+ (drop
+ (ref.func $C)
+ )
+ )
+
+ ;; YESTNH: (func $casts (type $funcref_=>_none) (param $x funcref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $A
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $casts (type $funcref_=>_none) (param $x funcref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $C
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $casts (param $x funcref)
+ ;; $A and $C have functions of their types, and references to them, so in
+ ;; theory we could optimize $B here.
+ (drop
+ (ref.cast $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C
+ (local.get $x)
+ )
+ )
+ )
+)
+
+;; Array subtyping, which is a TODO - for now we do nothing.
+(module
+ ;; YESTNH: (type $A (array (mut i32)))
+ ;; NO_TNH: (type $A (array (mut i32)))
+ (type $A (array (mut i32)))
+
+ ;; YESTNH: (type $B (array_subtype (mut i32) $A))
+ ;; NO_TNH: (type $B (array_subtype (mut i32) $A))
+ (type $B (array_subtype (mut i32) $A))
+
+ ;; YESTNH: (type $C (array_subtype (mut i32) $B))
+ ;; NO_TNH: (type $C (array_subtype (mut i32) $B))
+ (type $C (array_subtype (mut i32) $B))
+
+ ;; YESTNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; YESTNH: (global $A (ref $A) (array.new $A
+ ;; YESTNH-NEXT: (i32.const 10)
+ ;; YESTNH-NEXT: (i32.const 20)
+ ;; YESTNH-NEXT: ))
+ ;; NO_TNH: (type $anyref_=>_none (func (param anyref)))
+
+ ;; NO_TNH: (global $A (ref $A) (array.new $A
+ ;; NO_TNH-NEXT: (i32.const 10)
+ ;; NO_TNH-NEXT: (i32.const 20)
+ ;; NO_TNH-NEXT: ))
+ (global $A (ref $A) (array.new $A
+ (i32.const 10)
+ (i32.const 20)
+ ))
+
+ ;; YESTNH: (global $B (ref $B) (array.new $B
+ ;; YESTNH-NEXT: (i32.const 10)
+ ;; YESTNH-NEXT: (i32.const 20)
+ ;; YESTNH-NEXT: ))
+ ;; NO_TNH: (global $B (ref $B) (array.new $B
+ ;; NO_TNH-NEXT: (i32.const 10)
+ ;; NO_TNH-NEXT: (i32.const 20)
+ ;; NO_TNH-NEXT: ))
+ (global $B (ref $B) (array.new $B
+ (i32.const 10)
+ (i32.const 20)
+ ))
+
+ ;; YESTNH: (global $C (ref $C) (array.new $C
+ ;; YESTNH-NEXT: (i32.const 10)
+ ;; YESTNH-NEXT: (i32.const 20)
+ ;; YESTNH-NEXT: ))
+ ;; NO_TNH: (global $C (ref $C) (array.new $C
+ ;; NO_TNH-NEXT: (i32.const 10)
+ ;; NO_TNH-NEXT: (i32.const 20)
+ ;; NO_TNH-NEXT: ))
+ (global $C (ref $C) (array.new $C
+ (i32.const 10)
+ (i32.const 20)
+ ))
+
+ ;; YESTNH: (func $casts (type $anyref_=>_none) (param $x anyref)
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $A
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $B
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: (drop
+ ;; YESTNH-NEXT: (ref.cast $C
+ ;; YESTNH-NEXT: (local.get $x)
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; YESTNH-NEXT: )
+ ;; NO_TNH: (func $casts (type $anyref_=>_none) (param $x anyref)
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $A
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $B
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: (drop
+ ;; NO_TNH-NEXT: (ref.cast $C
+ ;; NO_TNH-NEXT: (local.get $x)
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ ;; NO_TNH-NEXT: )
+ (func $casts (param $x anyref)
+ (drop
+ (ref.cast $A
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $B
+ (local.get $x)
+ )
+ )
+ (drop
+ (ref.cast $C
+ (local.get $x)
+ )
+ )
+ )
+)