diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/lit/help/wasm-opt.test | 3 | ||||
-rw-r--r-- | test/lit/help/wasm2js.test | 3 | ||||
-rw-r--r-- | test/lit/passes/unsubtyping-casts.wast | 452 | ||||
-rw-r--r-- | test/lit/passes/unsubtyping.wast | 1633 |
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)) +) |