summaryrefslogtreecommitdiff
path: root/test/lit/passes
diff options
context:
space:
mode:
authorThomas Lively <tlively@google.com>2023-10-10 15:50:44 -0700
committerGitHub <noreply@github.com>2023-10-10 15:50:44 -0700
commit9d0740c8d84f822567bb4d08784238dd5a89b43f (patch)
treee915ad3675aec2e6cceb0c9b0e6573f5ebc3f6e4 /test/lit/passes
parent1cd81268627c71f36f45d6ef875dee84a79630f4 (diff)
downloadbinaryen-9d0740c8d84f822567bb4d08784238dd5a89b43f.tar.gz
binaryen-9d0740c8d84f822567bb4d08784238dd5a89b43f.tar.bz2
binaryen-9d0740c8d84f822567bb4d08784238dd5a89b43f.zip
Add an "unsubtyping" optimization (#5982)
Add a new pass that analyzes the module to find the minimal subtyping relation that is necessary to maintain the validity and semantics of the program and rewrites the types to use this minimal relation. Besides eliminating references to otherwise-unused intermediate types, this optimization should unlock significant additional optimizing power in other type optimizations that are constrained by having to maintain supertype validity, since after this new optimization there are fewer and more general supertypes. The analysis works by visiting each expression and module element to collect the subtypings that are required to maintain its validity, then, using that as a starting point, iteratively adding new subtypings required by type definitions and casts until reaching a fixed point.
Diffstat (limited to 'test/lit/passes')
-rw-r--r--test/lit/passes/unsubtyping-casts.wast452
-rw-r--r--test/lit/passes/unsubtyping.wast1633
2 files changed, 2085 insertions, 0 deletions
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))
+)