;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --closed-world --preserve-type-order \
;; RUN:     --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 $sub1 (sub (struct (field i32))))
 (type $sub1 (sub $super (struct i32)))
 ;; CHECK:       (type $sub2 (sub (struct (field f32))))
 (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 $sub1 (sub (struct (field i32))))
 (type $sub1 (sub $super (struct i32)))
 ;; CHECK:       (type $sub2 (sub (struct (field i32) (field 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) (item (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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (sub $super (struct)))
 (type $sub (sub $super (struct)))
 ;; CHECK:       (type $opt (sub (struct (field i32))))
 (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 (exact $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 (exact $sub)))
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:    (then
 ;; CHECK-NEXT:     (struct.new_default $sub)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (else
 ;; CHECK-NEXT:     (struct.new_default $sub)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $if
  (drop
   (if (result (ref $super))
    (i32.const 0)
    ;; This requires $sub <: $super.
    (then
     (struct.new $sub)
    )
    (else
     (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 (result (ref (exact $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 (exact $sub)))
 ;; CHECK-NEXT:    (drop
 ;; CHECK-NEXT:     (block $sub (result (ref (exact $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 (exact $sub)))
 ;; CHECK-NEXT:    (drop
 ;; CHECK-NEXT:     (block $sub (result (ref (exact $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 $super))))

 ;; CHECK:       (type $3 (func (result (ref $sub))))

 ;; CHECK:      (func $return-call (type $2) (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 $3) (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 1 1 funcref)
 (table $t 1 1 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 $super))))

 ;; CHECK:       (type $3 (func (result (ref $sub))))

 ;; CHECK:      (table $t 1 1 funcref)
 (table $t 1 1 funcref)

 ;; CHECK:      (func $return-call-indirect (type $2) (result (ref $super))
 ;; CHECK-NEXT:  (return_call_indirect $t (type $3)
 ;; 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 $super (sub (func)))
 (type $super (sub (func)))
 ;; CHECK:       (type $sub (sub (func)))
 (type $sub (sub $super (func)))

 ;; CHECK:      (table $t 1 1 (ref null $super))
 (table $t 1 1 (ref null $super))

 ;; 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 $sub1 (sub $super (struct)))
 (type $sub1 (sub $super (struct)))
 ;; CHECK:       (type $sub2 (sub $super (struct (field i32))))
 (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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (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 $super1 (sub (struct)))
  (type $super1 (sub (struct)))
  ;; CHECK:       (type $super2 (sub (struct)))
  (type $super2 (sub (struct)))
  ;; CHECK:       (type $sub1 (sub $super1 (struct)))
  (type $sub1 (sub $super1 (struct)))
  ;; CHECK:       (type $sub2 (sub $super2 (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 2
 ;; 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 2
    (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 1 1 (ref null $super))
 (table $t 1 1 (ref null $super))

 ;; 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 1 1 (ref null $super))
 (table $t 1 1 (ref null $super))

 ;; CHECK:      (func $table-fill (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-fill
  ;; 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:      (table $super 1 1 (ref null $super))
 (table $super 1 1 (ref null $super))

 ;; CHECK:      (table $sub 1 1 (ref null $sub))
 (table $sub 1 1 (ref null $sub))

 ;; CHECK:      (func $table-copy (type $2)
 ;; CHECK-NEXT:  (table.copy $super $sub
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $table-copy
  ;; This requires $sub <: $super.
  (table.copy $super $sub
   (i32.const 0)
   (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 $2 (func))

 ;; CHECK:      (table $super 1 1 (ref null $super))
 (table $super 1 1 (ref null $super))

 ;; CHECK:      (elem $sub (ref null $sub))
 (elem $sub (ref null $sub))

 ;; CHECK:      (func $table-copy (type $2)
 ;; CHECK-NEXT:  (table.init $super $sub
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $table-copy
  ;; This requires $sub <: $super.
  (table.init $super $sub
   (i32.const 0)
   (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 $2 (func))

 ;; CHECK:      (func $try (type $2)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (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 (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 (param (ref $super))))

 ;; CHECK:       (type $3 (func))

 ;; CHECK:      (tag $t (type $2) (param (ref $super)))
 (tag $t (param (ref $super)))

 ;; CHECK:      (func $throw (type $3)
 ;; 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 unreachable CallRef 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 unreachable CallRef 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)))
 (type $sub (sub $super (struct)))

 ;; CHECK:       (type $1 (func))

 ;; CHECK:      (func $br-on-cast (type $1)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block $l (result (ref (exact $super)))
 ;; CHECK-NEXT:    (br_on_cast $l (ref (exact $super)) (ref none)
 ;; 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 (exact $sub)))
 ;; CHECK-NEXT:    (br_on_cast_fail $l (ref (exact $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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (sub $super (struct)))
 (type $sub (sub $super (struct)))

 ;; CHECK:       (type $struct (sub (struct (field (ref null $super)))))
 (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 unreachable StructNew 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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (sub $super (struct)))
 (type $sub (sub $super (struct)))

 ;; CHECK:       (type $struct (sub (struct (field (mut (ref null $super))))))
 (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 unreachable StructSet 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 unreachable StructSet 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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (sub $super (struct)))
 (type $sub (sub $super (struct)))

 ;; CHECK:       (type $array (sub (array (ref null $super))))
 (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 unreachable ArrayNew 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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (sub $super (struct)))
 (type $sub (sub $super (struct)))

 ;; CHECK:       (type $array (sub (array (ref null $super))))
 (type $array (sub (array (ref null $super))))

 ;; CHECK:       (type $3 (func))

 ;; CHECK:      (elem $e (ref null $sub))
 (elem $e (ref null $sub))

 ;; CHECK:      (func $array-new-elem (type $3)
 ;; 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 unreachable ArrayNewElem 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 $super (sub (struct)))
 (type $super (sub (struct)))
 ;; CHECK:       (type $sub (sub $super (struct)))
 (type $sub (sub $super (struct)))

 ;; CHECK:       (type $array (sub (array (ref null $super))))
 (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 unreachable ArrayNewFixed 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 unreachable ArraySet 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 unreachable ArraySet 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 $super-array (sub (array (mut (ref null $super)))))
 (type $super-array (sub (array (mut (ref null $super)))))
 ;; CHECK:       (type $sub-array (sub (array (mut (ref null $sub)))))
 (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 ;; (replaces unreachable ArrayCopy 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:    (array.new_fixed $sub-array 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (block ;; (replaces unreachable ArrayCopy we can't emit)
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (array.new_fixed $super-array 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; 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:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; 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 ;; (replaces unreachable ArrayFill 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:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (block ;; (replaces unreachable ArrayFill 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:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; 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 $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:      (elem $e (ref null $sub))
 (elem $e (ref null $sub))

 ;; CHECK:      (func $array-init-elem (type $3)
 ;; 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 ;; (replaces unreachable ArrayInitElem 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:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (block ;; (replaces unreachable ArrayInitElem 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:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (drop
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (unreachable)
 ;; 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))
)

;; Regression test for a bug where we considered $super to be a public type
;; (because it was once in contention to appear in js-string-builtin
;; signatures), so we only updated $sub, but that caused $sub and $super to be
;; the same type, changing the behavior of the cast.
(module
 ;; CHECK:      (type $super (sub (array (mut i8))))
 (type $super (sub (array (mut i8))))
 (type $sub (sub $super (array (mut i8))))
 ;; CHECK:      (type $1 (func))

 ;; CHECK:      (export "test" (func $0))

 ;; CHECK:      (func $0 (type $1)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.cast (ref none)
 ;; CHECK-NEXT:    (array.new_default $super
 ;; CHECK-NEXT:     (i32.const 0)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $0 (export "test")
  (drop
   (ref.cast (ref $sub)
    (array.new_default $super
     (i32.const 0)
    )
   )
  )
 )
)

;; try_table
(module
 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $super (sub (func)))
  (type $super (sub (func)))
  ;; CHECK:       (type $sub (sub $super (func)))
  (type $sub (sub $super (func)))
 )

 ;; CHECK:       (type $2 (func (param (ref $sub))))

 ;; CHECK:       (type $3 (func (result (ref $super))))

 ;; CHECK:      (tag $tag (type $2) (param (ref $sub)))
 (tag $tag (param (ref $sub)))

 ;; CHECK:      (func $test (type $3) (result (ref $super))
 ;; CHECK-NEXT:  (block $label (result (ref $sub))
 ;; CHECK-NEXT:   (try_table (catch $tag $label)
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $test (result (ref $super))
  (block $label (result (ref $super))
   ;; Sending the contents of $tag to $label cause us to require $sub <: $super
   (try_table (catch $tag $label)
    (unreachable)
   )
  )
 )
)

;; Regression test for a crash on ifs with unreachable conditions and tuple arms.
(module
 ;; CHECK:      (type $0 (func (result i32 i64)))

 ;; CHECK:      (func $test (type $0) (result i32 i64)
 ;; CHECK-NEXT:  (if (type $0) (result i32 i64)
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:   (then
 ;; CHECK-NEXT:    (tuple.make 2
 ;; CHECK-NEXT:     (i32.const 0)
 ;; CHECK-NEXT:     (i64.const 1)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (else
 ;; CHECK-NEXT:    (tuple.make 2
 ;; CHECK-NEXT:     (i32.const 2)
 ;; CHECK-NEXT:     (i64.const 3)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $test (result i32 i64)
  (if (result i32 i64)
   (unreachable)
   (then
    (tuple.make 2
     (i32.const 0)
     (i64.const 1)
    )
   )
   (else
    (tuple.make 2
     (i32.const 2)
     (i64.const 3)
    )
   )
  )
 )
)

;; Regression test for assertion failure on incorrect updating of type tree
;; state.
(module
 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $0 (sub (struct)))
  (type $0 (sub (struct)))
  ;; CHECK:       (type $1 (sub $0 (struct (field (ref null $0)))))
  (type $1 (sub $0 (struct (field (ref null $0)))))
  ;; CHECK:       (type $2 (sub $1 (struct (field (ref null $3)))))
  (type $2 (sub $1 (struct (field (ref null $3)))))
  ;; CHECK:       (type $3 (sub $0 (struct)))
  (type $3 (sub $0 (struct)))
 )
 ;; CHECK:      (global $g (ref struct) (struct.new_default $2))
 (global $g (ref struct) (struct.new_default $2))
 ;; CHECK:      (global $g2 (ref null $1) (ref.null none))
 (global $g2 (ref null $1) (ref.null none))
 ;; CHECK:      (export "" (global $g2))
 (export "" (global $g2))
)

;; Regression for a mishandling of shared i31 with i31.get.
(module
 ;; CHECK:      (type $0 (func (result i32)))

 ;; CHECK:      (func $i31-get (type $0) (result i32)
 ;; CHECK-NEXT:  (i31.get_s
 ;; CHECK-NEXT:   (ref.i31_shared
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $i31-get (result i32)
  (i31.get_s
   (ref.i31_shared
    (i32.const 0)
   )
  )
 )
)

;; Even though our analysis has its own visitor for StructNew, it should still
;; be able to collect subtype constraints from StructNews.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $A (sub (struct (field (ref null $A)))))
  (type $A (sub (struct (field (ref null $A)))))
  ;; CHECK:       (type $B (sub $A (struct (field (ref null $A)))))
  (type $B (sub $A (struct (field (ref null $A)))))

  ;; CHECK:       (type $2 (func (param (ref null $B))))

  ;; CHECK:      (func $test (type $2) (param $B (ref null $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (local.get $B)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $B (ref null $B))
    (drop
      ;; This requires B <: A.
      (struct.new $A
        (local.get $B)
      )
    )
  )
)

;; extern.convert_any requires its input to remain a subtype of any.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $A (sub (struct)))
  (type $A (sub (struct)))
  ;; CHECK:       (type $B (sub $A (struct)))
  (type $B (sub $A (struct)))

  ;; CHECK:       (type $2 (func (result externref)))

  ;; CHECK:       (type $3 (func (param anyref)))

  ;; CHECK:      (func $test (type $2) (result externref)
  ;; CHECK-NEXT:  (extern.convert_any
  ;; CHECK-NEXT:   (struct.new_default $B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (result externref)
    (extern.convert_any
      (struct.new $B)
    )
  )

  ;; CHECK:      (func $cast (type $3) (param $0 anyref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref null $A)
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast (param anyref)
    (drop
      ;; Since $B flows into any, it must remain a subtype of $A to continue
      ;; passing this cast.
      (ref.cast (ref null $A)
        (local.get 0)
      )
    )
  )
)

;; Same as above with shared types.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $A (sub (shared (struct))))
  (type $A (sub (shared (struct))))
  ;; CHECK:       (type $B (sub $A (shared (struct))))
  (type $B (sub $A (shared (struct))))

  ;; CHECK:       (type $2 (func (result (ref null (shared extern)))))

  ;; CHECK:       (type $3 (func (param (ref null (shared any)))))

  ;; CHECK:      (func $test (type $2) (result (ref null (shared extern)))
  ;; CHECK-NEXT:  (extern.convert_any
  ;; CHECK-NEXT:   (struct.new_default $B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (result (ref null (shared extern)))
    (extern.convert_any
      (struct.new $B)
    )
  )

  ;; CHECK:      (func $cast (type $3) (param $0 (ref null (shared any)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref null $A)
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast (param (ref null (shared any)))
    (drop
      ;; Since $B flows into any, it must remain a subtype of $A to continue
      ;; passing this cast.
      (ref.cast (ref null $A)
        (local.get 0)
      )
    )
  )
)

;; Regression test for an assertion failure when br_if has a tuple value and
;; unreachable condition.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $A (sub (struct)))
  (type $A (sub (struct)))
  ;; CHECK:       (type $B (sub (struct)))
  (type $B (sub $A (struct)))
  ;; CHECK:       (type $C (sub $A (struct)))
  (type $C (sub $B (struct)))
  ;; CHECK:       (type $3 (func (param (ref $C)) (result (ref $A) (ref $A))))

  ;; CHECK:      (func $test (type $3) (param $C (ref $C)) (result (ref $A) (ref $A))
  ;; CHECK-NEXT:  (local $Bs (tuple (ref $B) (ref $B)))
  ;; CHECK-NEXT:  (block $l
  ;; CHECK-NEXT:   (local.tee $Bs
  ;; CHECK-NEXT:    (block
  ;; CHECK-NEXT:     (tuple.drop 2
  ;; CHECK-NEXT:      (tuple.make 2
  ;; CHECK-NEXT:       (local.get $C)
  ;; CHECK-NEXT:       (local.get $C)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $C (ref $C)) (result (ref $A) (ref $A))
    (local $Bs (tuple (ref $B) (ref $B)))
    (block $l (result (ref $A) (ref $A))
      (local.set $Bs
        ;; Because the br_if is unreachable, we do not record $C <: $B.
        (br_if $l
          (tuple.make 2
            (local.get $C)
            (local.get $C)
          )
          (unreachable)
        )
      )
    )
  )
)
