;; 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 -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 (param (ref $top))))

 ;; CHECK:      (func $cast (type $3) (param $top (ref $top))
 ;; 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:    (local.get $top)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $cast (param $top (ref $top))
  (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)
    (local.get $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 (param (ref $top))))

 ;; CHECK:      (func $cast (type $3) (param $top (ref $top))
 ;; 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:    (local.get $top)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $cast (param $top (ref $top))
  (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)
    (local.get $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 (param (ref $top))))

 ;; CHECK:      (func $cast (type $3) (param $top (ref $top))
 ;; 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:      (local.get $top)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $cast (param $top (ref $top))
  (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)
      (local.get $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 (param (ref $top))))

 ;; CHECK:      (func $cast (type $3) (param $top (ref $top))
 ;; 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:      (local.get $top)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (unreachable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $cast (param $top (ref $top))
  (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)
      (local.get $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:       (type $3 (func (param (ref $top))))

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

 ;; CHECK:      (elem declare func $bot)

 ;; CHECK:      (func $bot (type $bot)
 ;; CHECK-NEXT:  (nop)
 ;; CHECK-NEXT: )
 (func $bot (type $bot)
   (nop)
 )

 ;; CHECK:      (func $cast (type $3) (param $top (ref $top))
 ;; CHECK-NEXT:  (local $l (ref null $top))
 ;; CHECK-NEXT:  (local.set $l
 ;; CHECK-NEXT:   (ref.func $bot)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call_indirect $t (type $mid)
 ;; CHECK-NEXT:   (i32.const 0)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $cast (param $top (ref $top))
  (local $l (ref null $top))
  (local.set $l
   ;; Require $bot <: $top.
   (ref.func $bot)
  )
  ;; 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)
  )
 )
)

;; As above, but now with some ref.eq added. Those should not inhibit
;; optimizations: as before, $bot no longer needs to subtype from $mid (but
;; $mid must subtype from $top). ref.eq does add a requirement on subtyping
;; (that the type be a subtype of eq), but ref.eq does not actually flow the
;; inputs it receives anywhere, so that doesn't stop us from removing subtyping
;; from user types.
(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 (ref $top) (ref $mid) (ref $bot))))

 ;; CHECK:      (func $cast (type $3) (param $any anyref) (param $top (ref $top)) (param $mid (ref $mid)) (param $bot (ref $bot))
 ;; CHECK-NEXT:  (local $l anyref)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.eq
 ;; CHECK-NEXT:    (local.get $top)
 ;; CHECK-NEXT:    (local.get $mid)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.eq
 ;; CHECK-NEXT:    (local.get $top)
 ;; CHECK-NEXT:    (local.get $bot)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.eq
 ;; CHECK-NEXT:    (local.get $mid)
 ;; CHECK-NEXT:    (local.get $bot)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; 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) (param $top (ref $top)) (param $mid (ref $mid)) (param $bot (ref $bot))
  (local $l anyref)
  (drop
   (ref.eq
    (local.get $top)
    (local.get $mid)
   )
  )
  (drop
   (ref.eq
    (local.get $top)
    (local.get $bot)
   )
  )
  (drop
   (ref.eq
    (local.get $mid)
    (local.get $bot)
   )
  )
  (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)
   )
  )

  (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 (param (ref $topA) (ref $topB) (ref $topC))))

 ;; CHECK:      (func $cast (type $9) (param $topA (ref $topA)) (param $topB (ref $topB)) (param $topC (ref $topC))
 ;; 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:    (local.get $topA)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.cast (ref $midB)
 ;; CHECK-NEXT:    (local.get $topB)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.cast (ref $midC)
 ;; CHECK-NEXT:    (local.get $topC)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $cast (param $topA (ref $topA)) (param $topB (ref $topB)) (param $topC (ref $topC))
  (local $l (ref null $topA))
  (local.set $l
   ;; Require $botA <: $topA.
   (struct.new_default $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)
    (local.get $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)
    (local.get $topB)
   )
  )
  (drop
   ;; Same as before, but now for the C types. Now no types will able to be
   ;; optimized.
   (ref.cast (ref $midC)
    (local.get $topC)
   )
  )
 )
)

;; Exact casts do not impose requirements on subtypes of the destination type.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $top (sub (struct)))
    (type $top (sub (struct)))
    ;; CHECK:       (type $bot (sub (struct)))
    (type $bot (sub $top (struct)))
  )

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

  ;; CHECK:      (global $bot-sub-any anyref (struct.new_default $bot))
  (global $bot-sub-any anyref (struct.new $bot))

  ;; CHECK:      (func $ref.cast-exact (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref null (exact $top))
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref.cast-exact (param $any anyref)
    (drop
      (ref.cast (ref null (exact $top))
        (local.get $any)
      )
    )
  )
)

;; Same, but now with a br_on_cast.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $top (sub (struct)))
    (type $top (sub (struct)))
    ;; CHECK:       (type $bot (sub (struct)))
    (type $bot (sub $top (struct)))
  )

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

  ;; CHECK:      (global $bot-sub-any anyref (struct.new_default $bot))
  (global $bot-sub-any anyref (struct.new $bot))

  ;; CHECK:      (func $br_on_cast-exact (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $l (result anyref)
  ;; CHECK-NEXT:    (br_on_cast $l anyref (ref (exact $top))
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br_on_cast-exact (param $any anyref)
    (drop
      (block $l (result anyref)
        (br_on_cast $l anyref (ref (exact $top))
          (local.get $any)
        )
      )
    )
  )
)

;; Same, but now with a br_on_cast_fail.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $top (sub (struct)))
    (type $top (sub (struct)))
    ;; CHECK:       (type $bot (sub (struct)))
    (type $bot (sub $top (struct)))
  )

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

  ;; CHECK:      (global $bot-sub-any anyref (struct.new_default $bot))
  (global $bot-sub-any anyref (struct.new $bot))

  ;; CHECK:      (func $br_on_cast_fail-exact (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $l (result anyref)
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref (ref (exact $top))
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br_on_cast_fail-exact (param $any anyref)
    (drop
      (block $l (result anyref)
        (br_on_cast_fail $l anyref (ref (exact $top))
          (local.get $any)
        )
      )
    )
  )
)
