summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
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/unsubtyping-casts.wast452
-rw-r--r--test/lit/passes/unsubtyping.wast1633
4 files changed, 2091 insertions, 0 deletions
diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test
index 213b09e88..4607a94b9 100644
--- a/test/lit/help/wasm-opt.test
+++ b/test/lit/help/wasm-opt.test
@@ -492,6 +492,9 @@
;; CHECK-NEXT: --type-unfinalizing mark all types as non-final
;; CHECK-NEXT: (open)
;; CHECK-NEXT:
+;; CHECK-NEXT: --unsubtyping removes unnecessary subtyping
+;; CHECK-NEXT: relationships
+;; CHECK-NEXT:
;; CHECK-NEXT: --untee removes local.tees, replacing
;; CHECK-NEXT: them with sets and gets
;; CHECK-NEXT:
diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test
index 04c7d5577..0b041ddd3 100644
--- a/test/lit/help/wasm2js.test
+++ b/test/lit/help/wasm2js.test
@@ -451,6 +451,9 @@
;; CHECK-NEXT: --type-unfinalizing mark all types as non-final
;; CHECK-NEXT: (open)
;; CHECK-NEXT:
+;; CHECK-NEXT: --unsubtyping removes unnecessary subtyping
+;; CHECK-NEXT: relationships
+;; CHECK-NEXT:
;; CHECK-NEXT: --untee removes local.tees, replacing
;; CHECK-NEXT: them with sets and gets
;; CHECK-NEXT:
diff --git a/test/lit/passes/unsubtyping-casts.wast b/test/lit/passes/unsubtyping-casts.wast
new file mode 100644
index 000000000..8019efcd8
--- /dev/null
+++ b/test/lit/passes/unsubtyping-casts.wast
@@ -0,0 +1,452 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --closed-world --unsubtyping --remove-unused-types -all -S -o - | filecheck %s
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ (type $mid (sub $top (struct)))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (func $cast-optimizable (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref none)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast-optimizable
+ (drop
+ ;; Simply casting from $top to $mid does not require $mid <: $top, since we
+ ;; haven't shown that it's possible for $mid to inhabit $top. We should
+ ;; optimize this case.
+ (ref.cast (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ ;; Now the cast requires $mid <: $top so that a $bot value appearing in the
+ ;; $top location would still pass the cast to $mid.
+ (ref.cast (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.test (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ ;; Same as above, but with a ref.test.
+ (ref.test (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $mid))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast $l (ref $top) (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ (block $l (result (ref $mid))
+ ;; Same as above, but with a br_on_cast.
+ (drop
+ (br_on_cast $l anyref (ref $mid)
+ (struct.new $top)
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $top))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_on_cast_fail $l (ref $top) (ref $mid)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (struct.new $bot)
+ )
+ (drop
+ (block $l (result (ref $top))
+ ;; Same as above, but with a br_on_cast_fail.
+ (drop
+ (br_on_cast_fail $l anyref (ref $mid)
+ (struct.new $top)
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (func)))
+ (type $top (sub (func)))
+ ;; CHECK: (type $mid (sub $top (func)))
+ (type $mid (sub $top (func)))
+ ;; CHECK: (type $bot (sub $mid (func)))
+ (type $bot (sub $mid (func)))
+
+ ;; CHECK: (table $t 1 1 (ref null $top))
+ (table $t 1 1 (ref null $top))
+
+ ;; CHECK: (elem declare func $cast)
+
+ ;; CHECK: (func $cast (type $bot)
+ ;; CHECK-NEXT: (local $l (ref null $top))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (ref.func $cast)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_indirect $t (type $mid)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast (type $bot)
+ (local $l (ref null $top))
+ (local.set $l
+ ;; Require $bot <: $top.
+ (ref.func $cast)
+ )
+ ;; Same as above, but with a call_indirect
+ (call_indirect $t (type $mid)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $unrelated (sub (func)))
+ (type $unrelated (sub (func)))
+
+ ;; CHECK: (type $top (sub (func)))
+ (type $top (sub (func)))
+ ;; CHECK: (type $bot (sub $top (func)))
+ (type $bot (sub $top (func)))
+ )
+
+ ;; CHECK: (table $t 1 1 (ref null $bot))
+ (table $t 1 1 (ref null $bot))
+
+ ;; CHECK: (func $call-indirect (type $bot)
+ ;; CHECK-NEXT: (call_indirect $t (type $top)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (call_indirect $t (type $unrelated)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-indirect (type $bot)
+ ;; This cast is guaranteed to succeed, so this directly requires $bot <: $top.
+ (call_indirect $t (type $top)
+ (i32.const 0)
+ )
+ ;; This cast is guaraneed to fail. We should not introduce any subtype
+ ;; relationships that were not already there.
+ (call_indirect $t (type $unrelated)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub $mid (struct )))
+ (type $bot (sub $mid (struct)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $cast-optimizable (type $3)
+ ;; CHECK-NEXT: (local $l (ref null $mid))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $bot)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref none)
+ ;; CHECK-NEXT: (struct.new_default $top)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast-optimizable
+ (local $l (ref null $mid))
+ (local.set $l
+ ;; This time we require $bot <: $mid.
+ (struct.new $bot)
+ )
+ (drop
+ ;; Even though $bot can now inhabit $mid, it cannot inhabit $top, so it can
+ ;; never appear in this cast, so we do not require $mid <: $top or $bot <:
+ ;; $top.
+ (ref.cast (ref $mid)
+ (struct.new $top)
+ )
+ )
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $top (sub (struct )))
+ (type $top (sub (struct)))
+ ;; CHECK: (type $mid (sub $top (struct )))
+ (type $mid (sub $top (struct)))
+ ;; CHECK: (type $bot (sub (struct )))
+ (type $bot (sub $mid (struct)))
+ )
+
+ ;; CHECK: (type $3 (func (param anyref)))
+
+ ;; CHECK: (func $cast (type $3) (param $any anyref)
+ ;; CHECK-NEXT: (local $l anyref)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $bot)
+ ;; CHECK-NEXT: (local.get $any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $top)
+ ;; CHECK-NEXT: (local.get $any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $mid)
+ ;; CHECK-NEXT: (local.get $any)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $mid)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast (param $any anyref)
+ (local $l anyref)
+ (drop
+ ;; Cast any -> $bot
+ (ref.cast (ref $bot)
+ (local.get $any)
+ )
+ )
+ (drop
+ ;; Cast any -> $top
+ (ref.cast (ref $top)
+ (local.get $any)
+ )
+ )
+ (drop
+ ;; Cast any -> $mid
+ (ref.cast (ref $mid)
+ (local.get $any)
+ )
+ )
+
+ ;; Require $mid <: any. This will transitively require $mid <: $top and
+ ;; $top <: any, but $bot is unaffected.
+ (local.set $l
+ (struct.new $mid)
+ )
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $topC (sub (struct )))
+ (type $topC (sub (struct)))
+ ;; CHECK: (type $midC (sub $topC (struct )))
+ (type $midC (sub $topC (struct)))
+ ;; CHECK: (type $botC (sub $midC (struct )))
+ (type $botC (sub $midC (struct)))
+
+ ;; CHECK: (type $topB (sub (struct (field (ref null $topC)))))
+ (type $topB (sub (struct (ref null $topC))))
+ ;; CHECK: (type $midB (sub $topB (struct (field (ref null $botC)))))
+ (type $midB (sub $topB (struct (ref null $botC))))
+ ;; CHECK: (type $botB (sub $midB (struct (field (ref null $botC)))))
+ (type $botB (sub $midB (struct (ref null $botC))))
+
+ ;; CHECK: (type $topA (sub (struct (field (ref null $topB)))))
+ (type $topA (sub (struct (ref null $topB))))
+ ;; CHECK: (type $midA (sub $topA (struct (field (ref null $botB)))))
+ (type $midA (sub $topA (struct (ref null $botB))))
+ ;; CHECK: (type $botA (sub $midA (struct (field (ref null $botB)))))
+ (type $botA (sub $midA (struct (ref null $botB))))
+ )
+
+ ;; CHECK: (type $9 (func))
+
+ ;; CHECK: (func $cast (type $9)
+ ;; CHECK-NEXT: (local $l (ref null $topA))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $botA)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $midA)
+ ;; CHECK-NEXT: (struct.new_default $topA)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $midB)
+ ;; CHECK-NEXT: (struct.new_default $topB)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.cast (ref $midC)
+ ;; CHECK-NEXT: (struct.new_default $topC)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $cast
+ (local $l (ref null $topA))
+ (local.set $l
+ ;; Require $botA <: $topA.
+ (struct.new $botA)
+ )
+ (drop
+ ;; Now the cast requires $midA <: $topA so that a $botA value appearing in
+ ;; the $topA location would still pass the cast to $midA. This will
+ ;; transitively require $botB <: $topB.
+ (ref.cast (ref $midA)
+ (struct.new $topA)
+ )
+ )
+ (drop
+ ;; Same as before, but now for the B types. This requires $botC <: $topC, but
+ ;; only after the previous cast has already been analyzed.
+ (ref.cast (ref $midB)
+ (struct.new $topB)
+ )
+ )
+ (drop
+ ;; Same as before, but now for the C types. Now no types will able to be
+ ;; optimized.
+ (ref.cast (ref $midC)
+ (struct.new $topC)
+ )
+ )
+ )
+)
diff --git a/test/lit/passes/unsubtyping.wast b/test/lit/passes/unsubtyping.wast
new file mode 100644
index 000000000..a82d601cc
--- /dev/null
+++ b/test/lit/passes/unsubtyping.wast
@@ -0,0 +1,1633 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+;; RUN: foreach %s %t wasm-opt --closed-world --unsubtyping --remove-unused-types -all -S -o - | filecheck %s
+
+(module
+ ;; $sub1 and $sub2 should become parent types and $super should be removed.
+ (type $super (sub (struct)))
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $sub2 (sub (struct (field f32))))
+
+ ;; CHECK: (type $sub1 (sub (struct (field i32))))
+ (type $sub1 (sub $super (struct i32)))
+ (type $sub2 (sub $super (struct f32)))
+
+ ;; CHECK: (global $sub1 (ref $sub1) (struct.new_default $sub1))
+ (global $sub1 (ref $sub1) (struct.new_default $sub1))
+ ;; CHECK: (global $sub2 (ref $sub2) (struct.new_default $sub2))
+ (global $sub2 (ref $sub2) (struct.new_default $sub2))
+)
+
+(module
+ ;; Same result, but we start with $sub2 <: $sub1.
+ (type $super (sub (struct)))
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $sub2 (sub (struct (field i32) (field i32))))
+
+ ;; CHECK: (type $sub1 (sub (struct (field i32))))
+ (type $sub1 (sub $super (struct i32)))
+ (type $sub2 (sub $sub1 (struct i32 i32)))
+
+ ;; CHECK: (global $sub1 (ref $sub1) (struct.new_default $sub1))
+ (global $sub1 (ref $sub1) (struct.new_default $sub1))
+ ;; CHECK: (global $sub2 (ref $sub2) (struct.new_default $sub2))
+ (global $sub2 (ref $sub2) (struct.new_default $sub2))
+)
+
+(module
+ ;; CHECK: (type $super (sub (func)))
+ (type $super (sub (func)))
+ ;; CHECK: (type $sub (sub $super (func)))
+ (type $sub (sub $super (func)))
+
+ ;; Public types should not be changed.
+ ;; CHECK: (export "super" (func $super))
+
+ ;; CHECK: (export "sub" (func $sub))
+
+ ;; CHECK: (func $super (type $super)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $super (export "super") (type $super)
+ (unreachable)
+ )
+
+ ;; CHECK: (func $sub (type $sub)
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $sub (export "sub") (type $sub)
+ (unreachable)
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; A function body requires subtyping
+ ;; CHECK: (type $2 (func (result (ref $super))))
+
+ ;; CHECK: (func $foo (type $2) (result (ref $super))
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ (func $foo (result (ref $super))
+ (struct.new $sub)
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; A global initializer requires subtyping
+ ;; CHECK: (global $g1 (ref $super) (struct.new_default $sub))
+ (global $g1 (ref $super) (struct.new $sub))
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+ ;; CHECK: (type $subsub (sub $sub (struct )))
+ (type $subsub (sub $sub (struct)))
+
+ ;; CHECK: (table $t 1 1 (ref null $super))
+ (table $t 1 1 (ref null $super))
+
+ ;; An active element segment requires subtyping. So does an element segment
+ ;; element.
+ ;; CHECK: (elem $e (table $t) (i32.const 0) (ref null $sub) (struct.new_default $subsub))
+ (elem $e (table $t) (offset (i32.const 0)) (ref null $sub) (struct.new $subsub))
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $X (sub (struct )))
+ (type $X (sub (struct)))
+ ;; CHECK: (type $Y (sub $X (struct )))
+ (type $Y (sub $X (struct)))
+
+ ;; CHECK: (type $A (sub (struct (field (ref null $X)))))
+ (type $A (sub (struct (ref null $X))))
+ ;; CHECK: (type $B (sub $A (struct (field (ref null $Y)))))
+ (type $B (sub $A (struct (ref null $Y))))
+
+ ;; Requiring B <: A also requires X <: Y
+ ;; CHECK: (global $g (ref $A) (struct.new_default $B))
+ (global $g (ref $A) (struct.new_default $B))
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $X (sub (struct )))
+ (type $X (sub (struct)))
+ ;; CHECK: (type $Y (sub $X (struct )))
+ (type $Y (sub $X (struct)))
+
+ ;; CHECK: (type $A (sub (array (ref null $X))))
+ (type $A (sub (array (field (ref null $X)))))
+ ;; CHECK: (type $B (sub $A (array (ref null $Y))))
+ (type $B (sub $A (array (field (ref null $Y)))))
+
+ ;; Transitive dependencies through an array.
+ ;; CHECK: (global $g (ref $A) (array.new_default $B
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: ))
+ (global $g (ref $A) (array.new_default $B (i32.const 0)))
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $X (sub (struct )))
+ (type $X (sub (struct)))
+ ;; CHECK: (type $Y (sub $X (struct )))
+ (type $Y (sub $X (struct)))
+
+ ;; CHECK: (type $X' (sub (struct )))
+ (type $X' (sub (struct)))
+ ;; CHECK: (type $Y' (sub $X' (struct )))
+ (type $Y' (sub $X' (struct)))
+
+ ;; CHECK: (type $A (sub (func (param (ref $Y')) (result (ref $X)))))
+ (type $A (sub (func (param (ref $Y')) (result (ref $X)))))
+ ;; CHECK: (type $B (sub $A (func (param (ref $X')) (result (ref $Y)))))
+ (type $B (sub $A (func (param (ref $X')) (result (ref $Y)))))
+ )
+
+ ;; Transitive dependencies through a function type.
+ ;; CHECK: (global $g (ref null $A) (ref.func $foo))
+ (global $g (ref null $A) (ref.func $foo))
+
+ ;; CHECK: (func $foo (type $B) (param $0 (ref $X')) (result (ref $Y))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $foo (type $B)
+ (unreachable)
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $block-fallthrough (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $super))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (br_if $l
+ ;; CHECK-NEXT: (struct.new_default $super)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-fallthrough
+ (drop
+ (block $l (result (ref $super))
+ (drop
+ (br_if $l
+ (struct.new $super)
+ (i32.const 0)
+ )
+ )
+ ;; This requires $sub <: $super
+ (struct.new $sub)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $opt (sub (struct (field i32))))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+ (type $opt (sub $super (struct i32)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $block-br (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $super))
+ ;; CHECK-NEXT: (br $l
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $other (result (ref $opt))
+ ;; CHECK-NEXT: (br $other
+ ;; CHECK-NEXT: (struct.new_default $opt)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.new_default $super)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $block-br
+ (drop
+ (block $l (result (ref $super))
+ (br $l
+ ;; This requires $sub <: $super
+ (struct.new $sub)
+ )
+ (drop
+ (block $other (result (ref $opt))
+ (br $other
+ ;; But this doesn't require anything, so $opt will be optimized.
+ (struct.new_default $opt)
+ )
+ )
+ )
+ (struct.new $super)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $if (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (if (result (ref $sub))
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $if
+ (drop
+ (if (result (ref $super))
+ (i32.const 0)
+ ;; This requires $sub <: $super.
+ (struct.new $sub)
+ (struct.new $sub)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $loop (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (loop $loop-in (result (ref $sub))
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop
+ (drop
+ (loop (result (ref $super))
+ ;; This requires $sub <: $super.
+ (struct.new $sub)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $loop (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $super (result (ref $sub))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $sub (result (ref $sub))
+ ;; CHECK-NEXT: (br_table $super $sub
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $loop
+ (drop
+ (block $super (result (ref $super))
+ (drop
+ (block $sub (result (ref $sub))
+ ;; This requires $sub <: $super.
+ (br_table $super $sub
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $br-table (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $super (result (ref $sub))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $sub (result (ref $sub))
+ ;; CHECK-NEXT: (br_table $sub $super
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-table
+ (drop
+ (block $super (result (ref $super))
+ (drop
+ (block $sub (result (ref $sub))
+ ;; Same as above with labels reversed. This requires $sub <: $super.
+ (br_table $sub $super
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ )
+ )
+ (unreachable)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func (param (ref $super))))
+
+ ;; CHECK: (func $call (type $2) (param $0 (ref $super))
+ ;; CHECK-NEXT: (call $call
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call (param (ref $super))
+ ;; This requires $sub <: $super
+ (call $call
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func (result (ref $sub))))
+
+ ;; CHECK: (type $3 (func (result (ref $super))))
+
+ ;; CHECK: (func $return-call (type $3) (result (ref $super))
+ ;; CHECK-NEXT: (return_call $callee)
+ ;; CHECK-NEXT: )
+ (func $return-call (result (ref $super))
+ ;; This requires $sub <: $super
+ (return_call $callee)
+ )
+
+ ;; CHECK: (func $callee (type $2) (result (ref $sub))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $callee (result (ref $sub))
+ (unreachable)
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func (param (ref $super))))
+
+ ;; CHECK: (table $t 0 funcref)
+ (table $t funcref)
+
+ ;; CHECK: (func $call-indirect (type $2) (param $0 (ref $super))
+ ;; CHECK-NEXT: (call_indirect $t (type $2)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-indirect (param (ref $super))
+ ;; This requires $sub <: $super.
+ (call_indirect $t (param (ref $super))
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func (result (ref $sub))))
+
+ ;; CHECK: (type $3 (func (result (ref $super))))
+
+ ;; CHECK: (table $t 0 funcref)
+ (table $t funcref)
+
+ ;; CHECK: (func $return-call-indirect (type $3) (result (ref $super))
+ ;; CHECK-NEXT: (return_call_indirect $t (type $2)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return-call-indirect (result (ref $super))
+ ;; This requires $sub <: $super.
+ (return_call_indirect $t (result (ref $sub))
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $sub (sub (func)))
+
+ ;; CHECK: (type $super (sub (func)))
+ (type $super (sub (func)))
+ (type $sub (sub $super (func)))
+
+ ;; CHECK: (table $t 0 (ref null $super))
+ (table $t (ref null $super) 1 1)
+
+ ;; CHECK: (func $call-indirect-table (type $sub)
+ ;; CHECK-NEXT: (call_indirect $t (type $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-indirect-table (type $sub)
+ ;; This does *not* require $sub <: $super on its own, although if that
+ ;; subtyping is not required at all, then it must be impossible for the table
+ ;; to contain a $sub and this call will trap.
+ (call_indirect $t (type $sub)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $local-set (type $2)
+ ;; CHECK-NEXT: (local $l (ref null $super))
+ ;; CHECK-NEXT: (local.set $l
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $local-set
+ (local $l (ref null $super))
+ ;; This requires $sub <: $super.
+ (local.set $l
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $local-tee (type $2)
+ ;; CHECK-NEXT: (local $l (ref null $super))
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (local.tee $l
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $local-tee
+ (local $l (ref null $super))
+ (drop
+ ;; This requires $sub <: $super.
+ (local.tee $l
+ (struct.new $sub)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (global $g (mut (ref null $super)) (ref.null none))
+ (global $g (mut (ref null $super)) (ref.null none))
+
+ ;; CHECK: (func $global-set (type $2)
+ ;; CHECK-NEXT: (global.set $g
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $global-set
+ ;; This requires $sub <: $super.
+ (global.set $g
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub2 (sub $super (struct (field i32))))
+
+ ;; CHECK: (type $sub1 (sub $super (struct )))
+ (type $sub1 (sub $super (struct)))
+ (type $sub2 (sub $super (struct i32)))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $select (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (select (result (ref $super))
+ ;; CHECK-NEXT: (struct.new_default $sub1)
+ ;; CHECK-NEXT: (struct.new_default $sub2)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $select
+ (drop
+ ;; This requires $sub1 <: $super and $sub2 <: super.
+ (select (result (ref $super))
+ (struct.new $sub1)
+ (struct.new_default $sub2)
+ (i32.const 0)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func (result (ref $super))))
+
+ ;; CHECK: (func $return (type $2) (result (ref $super))
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return (result (ref $super))
+ (return
+ ;; This requires $sub <: $super.
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $sub (sub (struct )))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $return-none (type $2)
+ ;; CHECK-NEXT: (local $super (ref null $super))
+ ;; CHECK-NEXT: (local $sub (ref null $sub))
+ ;; CHECK-NEXT: (return)
+ ;; CHECK-NEXT: )
+ (func $return-none
+ (local $super (ref null $super))
+ (local $sub (ref null $sub))
+ ;; Check that we don't get confused by bare returns.
+ (return)
+ )
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super2 (sub (struct )))
+
+ ;; CHECK: (type $sub2 (sub $super2 (struct )))
+
+ ;; CHECK: (type $super1 (sub (struct )))
+ (type $super1 (sub (struct)))
+ (type $super2 (sub (struct)))
+ ;; CHECK: (type $sub1 (sub $super1 (struct )))
+ (type $sub1 (sub $super1 (struct)))
+ (type $sub2 (sub $super2 (struct)))
+ )
+
+ ;; CHECK: (type $4 (func (result (ref $super1) (ref $super2))))
+
+ ;; CHECK: (func $return-many (type $4) (result (ref $super1) (ref $super2))
+ ;; CHECK-NEXT: (return
+ ;; CHECK-NEXT: (tuple.make
+ ;; CHECK-NEXT: (struct.new_default $sub1)
+ ;; CHECK-NEXT: (struct.new_default $sub2)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return-many (result (ref $super1) (ref $super2))
+ ;; This requires $sub1 <: $super1 and $sub2 <: super2.
+ (return
+ (tuple.make
+ (struct.new $sub1)
+ (struct.new $sub2)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (table $t 0 (ref null $super))
+ (table $t (ref null $super) 1 1)
+
+ ;; CHECK: (func $table-set (type $2)
+ ;; CHECK-NEXT: (table.set $t
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $table-set
+ ;; This requires $sub <: $super.
+ (table.set $t
+ (i32.const 0)
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (table $t 0 (ref null $super))
+ (table $t (ref null $super) 1 1)
+
+ ;; CHECK: (func $table-set (type $2)
+ ;; CHECK-NEXT: (table.fill $t
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $table-set
+ ;; This requires $sub <: $super.
+ (table.fill $t
+ (i32.const 0)
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $try (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (try $try (result (ref $super))
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch_all
+ ;; CHECK-NEXT: (struct.new_default $super)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try
+ (drop
+ (try (result (ref $super))
+ (do
+ ;; This requires $sub <: $super.
+ (struct.new $sub)
+ )
+ (catch_all
+ (struct.new $super)
+ )
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $try-catch (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (try $try (result (ref $super))
+ ;; CHECK-NEXT: (do
+ ;; CHECK-NEXT: (struct.new_default $super)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (catch_all
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $try-catch
+ (drop
+ (try (result (ref $super))
+ (do
+ (struct.new $super)
+ )
+ (catch_all
+ ;; This requires $sub <: $super.
+ (struct.new $sub)
+ )
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (type $3 (func (param (ref $super))))
+
+ ;; CHECK: (type $4 (func (param (ref $super))))
+
+ ;; CHECK: (tag $t (param (ref $super)))
+ (tag $t (param (ref $super)))
+
+ ;; CHECK: (func $throw (type $2)
+ ;; CHECK-NEXT: (throw $t
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $throw
+ (throw $t
+ ;; This requires $sub <: $super.
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $f (func (param (ref $super))))
+ (type $f (func (param (ref $super))))
+
+ ;; CHECK: (elem declare func $call-ref)
+
+ ;; CHECK: (func $call-ref (type $f) (param $0 (ref $super))
+ ;; CHECK-NEXT: (call_ref $f
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (ref.func $call-ref)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.null nofunc)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $call-ref (type $f) (param (ref $super))
+ ;; This requires $sub <: $super.
+ (call_ref $f
+ (struct.new $sub)
+ (ref.func $call-ref)
+ )
+ ;; This should not trip us up.
+ (call_ref $f
+ (struct.new $sub)
+ (unreachable)
+ )
+ ;; Nor should this.
+ (call_ref $f
+ (struct.new $sub)
+ (ref.null nofunc)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $f (func (result (ref $sub))))
+ (type $f (func (result (ref $sub))))
+
+ ;; CHECK: (type $3 (func (result (ref $super))))
+
+ ;; CHECK: (elem declare func $callee)
+
+ ;; CHECK: (func $return-call-ref (type $3) (result (ref $super))
+ ;; CHECK-NEXT: (return_call_ref $f
+ ;; CHECK-NEXT: (ref.func $callee)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $return-call-ref (result (ref $super))
+ ;; This requires $sub <: $super.
+ (return_call_ref $f
+ (ref.func $callee)
+ )
+ )
+
+ ;; CHECK: (func $callee (type $f) (result (ref $sub))
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ (func $callee (result (ref $sub))
+ (unreachable)
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $br-on-non-null (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $super))
+ ;; CHECK-NEXT: (br_on_non_null $l
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (struct.new_default $super)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-on-non-null
+ (drop
+ (block $l (result (ref $super))
+ ;; This requires $sub <: $super.
+ (br_on_non_null $l
+ (struct.new $sub)
+ )
+ (struct.new $super)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $br-on-cast (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $super))
+ ;; CHECK-NEXT: (br_on_cast $l (ref $super) (ref $sub)
+ ;; CHECK-NEXT: (struct.new_default $super)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-on-cast
+ (drop
+ (block $l (result (ref $super))
+ ;; This requires $sub <: $super.
+ (br_on_cast $l anyref (ref $sub)
+ (struct.new $super)
+ )
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $2 (func))
+
+ ;; CHECK: (func $br-on-cast-fail (type $2)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block $l (result (ref $sub))
+ ;; CHECK-NEXT: (br_on_cast_fail $l (ref $sub) (ref none)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $br-on-cast-fail
+ (drop
+ (block $l (result (ref $super))
+ ;; This requires $sub <: $super.
+ (br_on_cast_fail $l anyref (ref none)
+ (struct.new $sub)
+ )
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $struct (sub (struct (field (ref null $super)))))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ (type $struct (sub (struct (ref null $super))))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $struct-new (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new $struct
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $struct)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-new
+ (drop
+ ;; This requires $sub <: $super.
+ (struct.new $struct
+ (struct.new $sub)
+ )
+ )
+ (drop
+ ;; This should not trip us up.
+ (struct.new_default $struct)
+ )
+ ;; Nor should this.
+ (struct.new $struct
+ (unreachable)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $struct (sub (struct (field (mut (ref null $super))))))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ (type $struct (sub (struct (mut (ref null $super)))))
+
+ ;; CHECK: (type $3 (func (param (ref null $struct))))
+
+ ;; CHECK: (func $struct-set (type $3) (param $struct (ref null $struct))
+ ;; CHECK-NEXT: (struct.set $struct 0
+ ;; CHECK-NEXT: (local.get $struct)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $struct-set (param $struct (ref null $struct))
+ ;; This requires $sub <: $super.
+ (struct.set $struct 0
+ (local.get $struct)
+ (struct.new $sub)
+ )
+ ;; This should not trip us up.
+ (struct.set $struct 0
+ (unreachable)
+ (struct.new $sub)
+ )
+ ;; Nor should this.
+ (struct.set $struct 0
+ (ref.null none)
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $array (sub (array (ref null $super))))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ (type $array (sub (array (ref null $super))))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $array-new (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (array.new $array
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (array.new_default $array
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-new
+ (drop
+ ;; This requires $sub <: $super.
+ (array.new $array
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ )
+ (drop
+ ;; This should not trip us up.
+ (array.new_default $array
+ (i32.const 0)
+ )
+ )
+ (drop
+ ;; Nor should this.
+ (array.new $array
+ (unreachable)
+ (i32.const 0)
+ )
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $array (sub (array (ref null $super))))
+
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ (type $array (sub (array (ref null $super))))
+
+ ;; CHECK: (elem $e (ref null $sub))
+ (elem $e (ref null $sub))
+
+ ;; CHECK: (func $array-new-elem (type $1)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (array.new_elem $array $e
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-new-elem
+ (drop
+ ;; This requires $sub <: $super.
+ (array.new_elem $array $e
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+ ;; This should not trip us up.
+ (array.new_elem $array $e
+ (unreachable)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $array (sub (array (ref null $super))))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ (type $array (sub (array (ref null $super))))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $array-new-fixed (type $3)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (array.new_fixed $array 1
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-new-fixed
+ (drop
+ ;; This requires $sub <: $super.
+ (array.new_fixed $array 1
+ (struct.new $sub)
+ )
+ )
+ ;; This should not trip us up.
+ (array.new_fixed $array 1
+ (unreachable)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $array (sub (array (mut (ref null $super)))))
+ (type $array (sub (array (mut (ref null $super)))))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $array-set (type $3)
+ ;; CHECK-NEXT: (array.set $array
+ ;; CHECK-NEXT: (array.new_fixed $array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block ;; (replaces something unreachable we can't emit)
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (drop
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-set
+ ;; This requires $sub <: $super.
+ (array.set $array
+ (array.new_fixed $array 0)
+ (i32.const 0)
+ (struct.new $sub)
+ )
+ ;; This should not trip us up.
+ (array.set $array
+ (unreachable)
+ (i32.const 0)
+ (struct.new $sub)
+ )
+ ;; Nor should this.
+ (array.set $array
+ (ref.null none)
+ (i32.const 0)
+ (struct.new $sub)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $sub-array (sub (array (mut (ref null $sub)))))
+
+ ;; CHECK: (type $super-array (sub (array (mut (ref null $super)))))
+ (type $super-array (sub (array (mut (ref null $super)))))
+ (type $sub-array (sub (array (mut (ref null $sub)))))
+
+ ;; CHECK: (type $4 (func))
+
+ ;; CHECK: (func $array-copy (type $4)
+ ;; CHECK-NEXT: (array.copy $super-array $sub-array
+ ;; CHECK-NEXT: (array.new_fixed $super-array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (array.new_fixed $sub-array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (array.new_fixed $sub-array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (array.new_fixed $super-array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-copy
+ ;; This requires $sub <: $super.
+ (array.copy $super-array $sub-array
+ (array.new_fixed $super-array 0)
+ (i32.const 0)
+ (array.new_fixed $sub-array 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ ;; This should not trip us up.
+ (array.copy $super-array $sub-array
+ (unreachable)
+ (i32.const 0)
+ (array.new_fixed $sub-array 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ ;; Nor should this.
+ (array.copy $super-array $sub-array
+ (array.new_fixed $super-array 0)
+ (i32.const 0)
+ (ref.null none)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ ;; CHECK: (type $array (sub (array (mut (ref null $super)))))
+ (type $array (sub (array (mut (ref null $super)))))
+
+ ;; CHECK: (type $3 (func))
+
+ ;; CHECK: (func $array-fill (type $3)
+ ;; CHECK-NEXT: (array.fill $array
+ ;; CHECK-NEXT: (array.new_fixed $array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (struct.new_default $sub)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-fill
+ ;; This requires $sub <: $super.
+ (array.fill $array
+ (array.new_fixed $array 0)
+ (i32.const 0)
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ ;; This should not trip us up.
+ (array.fill $array
+ (unreachable)
+ (i32.const 0)
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ ;; Nor should this.
+ (array.fill $array
+ (ref.null none)
+ (i32.const 0)
+ (struct.new $sub)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $array (sub (array (mut (ref null $super)))))
+
+ ;; CHECK: (type $1 (func))
+
+ ;; CHECK: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $super (struct)))
+
+ (type $array (sub (array (mut (ref null $super)))))
+
+ ;; CHECK: (elem $e (ref null $sub))
+ (elem $e (ref null $sub))
+
+ ;; CHECK: (func $array-init-elem (type $1)
+ ;; CHECK-NEXT: (array.init_elem $array $e
+ ;; CHECK-NEXT: (array.new_fixed $array 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (unreachable)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: (block
+ ;; CHECK-NEXT: (ref.null none)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: (i32.const 0)
+ ;; CHECK-NEXT: )
+ ;; CHECK-NEXT: )
+ (func $array-init-elem
+ ;; This requires $sub <: $super.
+ (array.init_elem $array $e
+ (array.new_fixed $array 0)
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ ;; This should not trip us up.
+ (array.init_elem $array $e
+ (unreachable)
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ ;; Nor should this.
+ (array.init_elem $array $e
+ (ref.null none)
+ (i32.const 0)
+ (i32.const 0)
+ (i32.const 0)
+ )
+ )
+)
+
+(module
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct )))
+ (type $super (sub (struct)))
+ (type $mid (sub $super (struct)))
+ ;; CHECK: (type $sub (sub $super (struct )))
+ (type $sub (sub $mid (struct)))
+
+ ;; $sub <: $super, but it no longer needs to be related to $mid.
+ ;; CHECK: (global $g (ref $super) (struct.new_default $sub))
+ (global $g (ref $super) (struct.new $sub))
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct (field (ref null $mid)))))
+ (type $super (sub (struct (ref null $mid))))
+ ;; CHECK: (type $mid (sub $super (struct (field (ref null $mid)) (field i32))))
+ (type $mid (sub $super (struct (ref null $mid) i32)))
+ ;; CHECK: (type $sub (sub $mid (struct (field (ref null $sub)) (field i32) (field i32))))
+ (type $sub (sub $mid (struct (ref null $sub) i32 i32)))
+ )
+
+ ;; Same as above, but now that transitively requires $sub <: $mid, so we don't
+ ;; end up changing anything.
+ ;; CHECK: (global $g (ref $super) (struct.new_default $sub))
+ (global $g (ref $super) (struct.new_default $sub))
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $super (sub (struct (field (ref null $super)))))
+ (type $super (sub (struct (ref null $super))))
+ ;; CHECK: (type $mid (sub $super (struct (field (ref null $super)) (field i32))))
+ (type $mid (sub $super (struct (ref null $super) i32)))
+ ;; CHECK: (type $sub (sub $mid (struct (field (ref null $sub)) (field i32) (field i32))))
+ (type $sub (sub $mid (struct (ref null $sub) i32 i32)))
+ )
+
+ ;; Similar, but now we directly require that $sub <: $mid and transitively
+ ;; require that $sub <: $super, so again we cannot change anything.
+ ;; CHECK: (global $g (ref $mid) (struct.new_default $sub))
+ (global $g (ref $mid) (struct.new_default $sub))
+)
+
+(module
+ (rec
+ ;; CHECK: (rec
+ ;; CHECK-NEXT: (type $X (sub (struct )))
+ (type $X (sub (struct)))
+ ;; CHECK: (type $Y (sub $X (struct )))
+ (type $Y (sub $X (struct)))
+
+ ;; CHECK: (type $super (sub (struct (field (ref null $mid)))))
+ (type $super (sub (struct (ref null $mid))))
+ ;; CHECK: (type $mid (sub $super (struct (field (ref null $sub)) (field (ref null $X)))))
+ (type $mid (sub $super (struct (ref null $sub) (ref null $X))))
+ ;; CHECK: (type $sub (sub $mid (struct (field (ref null $sub)) (field (ref null $Y)))))
+ (type $sub (sub $mid (struct (ref null $sub) (ref null $Y))))
+ )
+
+ ;; Require $sub <: $super, which transitively requires $sub <: $mid, which then
+ ;; requires $Y <: $X. This only works because we put $sub back in the work list
+ ;; when we find a more refined supertype for it.
+ ;; CHECK: (global $g (ref $super) (struct.new_default $sub))
+ (global $g (ref $super) (struct.new_default $sub))
+)