;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt -all --gufa -tnh -S -o - | filecheck %s

(module
  ;; CHECK:      (type $0 (func (param funcref funcref funcref funcref)))

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

  ;; CHECK:      (import "a" "b" (global $unknown-i32 i32))
  (import "a" "b" (global $unknown-i32 i32))

  ;; CHECK:      (import "a" "b" (global $unknown-funcref1 funcref))
  (import "a" "b" (global $unknown-funcref1 funcref))

  ;; CHECK:      (import "a" "b" (global $unknown-funcref2 funcref))
  (import "a" "b" (global $unknown-funcref2 funcref))

  ;; CHECK:      (import "a" "b" (global $unknown-nn-func1 (ref func)))
  (import "a" "b" (global $unknown-nn-func1 (ref func)))

  ;; CHECK:      (import "a" "b" (global $unknown-nn-func2 (ref func)))
  (import "a" "b" (global $unknown-nn-func2 (ref func)))

  ;; CHECK:      (func $called (type $0) (param $x funcref) (param $no-cast funcref) (param $y funcref) (param $z funcref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $z)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x funcref) (param $no-cast funcref) (param $y funcref) (param $z funcref)
    ;; All but the second parameter are cast here, which allows some
    ;; optimization in the caller. Nothing significant changes here in this
    ;; function.
    (drop
      (ref.cast (ref func)
        (local.get $x)
      )
    )
    (drop
      (ref.cast (ref func)
        (local.get $y)
      )
    )
    (drop
      (ref.cast (ref func)
        (local.get $z)
      )
    )
  )

  ;; CHECK:      (func $caller (type $1)
  ;; CHECK-NEXT:  (local $f funcref)
  ;; CHECK-NEXT:  (local.set $f
  ;; CHECK-NEXT:   (select (result funcref)
  ;; CHECK-NEXT:    (global.get $unknown-funcref1)
  ;; CHECK-NEXT:    (global.get $unknown-funcref2)
  ;; CHECK-NEXT:    (global.get $unknown-i32)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast funcref
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.get $f)
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.get $f)
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref func)
  ;; CHECK-NEXT:    (local.get $f)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller
    (local $f funcref)

    ;; Fill the local with an unknown value (so that trivial inference doesn't
    ;; optimize away the thing we care about below).
    (local.set $f
      (select
        (global.get $unknown-funcref1)
        (global.get $unknown-funcref2)
        (global.get $unknown-i32)
      )
    )

    ;; All but the third parameter are cast here. The cast has no effect by
    ;; itself as the type is funcref, but GUFA will refine casts when it can
    ;; (but not add a new cast, which might not be worth it).
    ;;
    ;; Specifically here, the first and last cast can be refined, since those
    ;; are cast both here and in the called function. Those casts will lose the
    ;; "null" and become non-nullable.
    (call $called
      (ref.cast funcref
        (local.get $f)
      )
      (ref.cast funcref
        (local.get $f)
      )
      (local.get $f)
      (ref.cast funcref
        (local.get $f)
      )
    )

    ;; Another call, but with different casts.
    (call $called
      (ref.cast funcref ;; this is now non-nullable, and will not change
        (local.get $f)
      )
      (local.get $f) ;; this is not cast, and will not change.
      (ref.cast funcref
        (local.get $f) ;; this is now cast, and will be optimized.
      )
      (ref.cast funcref ;; this is the same as before, and will be optimized.
        (local.get $f)
      )
    )

    ;; Test that we do not error in unreachable code.
    (call $called
      (unreachable)
      (unreachable)
      (unreachable)
      (unreachable)
    )
  )
)

(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $maker (type $2)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $maker
    ;; A always contains 10, and B always contains 20.
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B
        (i32.const 20)
      )
    )
  )

  ;; CHECK:      (func $called (type $3) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    ;; Cast the input to a $B, which will help the caller.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $any anyref)
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.test (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (local $x (ref null $A))
    ;; The called function casts to $B. This lets us infer the value of the
    ;; fallthrough ref.cast, which will turn into $B. Furthermore, that then
    ;; tells us what is written into the local $x, and the forward flow
    ;; analysis will use that fact in the local.get $x below.
    (call $called
      (local.tee $x
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
    )
    ;; We can't infer anything here at the moment, but a more sophisticated
    ;; analysis could. (Other passes can help here, however, by using $x where
    ;; $any appears.)
    (drop
      (struct.get $A 0
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
    )
    (drop
      (ref.test (ref $B)
        (local.get $any)
      )
    )
    ;; We know that $x must contain $B, so this can be inferred to be 20, and
    ;; the ref.is to 1.
    (drop
      (struct.get $A 0
        (local.get $x)
      )
    )
    (drop
      (ref.test (ref $B)
        (local.get $x)
      )
    )
  )
)

;; A local.tee by itself, without a cast.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $maker (type $3)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $maker
    ;; A always contains 10, and B always contains 20.
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B
        (i32.const 20)
      )
    )
  )

  ;; CHECK:      (func $called (type $2) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    ;; Cast the input to a $B, which will help the caller.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $2) (param $a (ref null $A))
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (local.get $a)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 20)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $a (ref null $A))
    (local $x (ref null $A))
    ;; The change compared to before is that we only have a local.tee here, and
    ;; no ref.cast. We can still infer the type of the tee's value, and
    ;; therefore the type of $x when it is read below, and optimize there.
    (call $called
      (local.tee $x
        (local.get $a)
      )
    )
    ;; This can be inferred to be 20.
    (drop
      (struct.get $A 0
        (local.get $x)
      )
    )
  )
)

;; As above, but add a local.tee etc. in the called function.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

  ;; CHECK:      (global $global (mut i32) (i32.const 0))
  (global $global (mut i32) (i32.const 0))

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $2) (param $x (ref null $A))
  ;; CHECK-NEXT:  (local $local (ref null $A))
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 42)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (global.set $global
  ;; CHECK-NEXT:   (i32.const 1337)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.tee $local
  ;; CHECK-NEXT:     (local.get $x)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (local $local (ref null $A))
    ;; Some nops and such do not bother us. Even a side effect like setting a
    ;; global does not.
    (nop)
    (drop
      (i32.const 42)
    )
    (global.set $global
      (i32.const 1337)
    )
    (drop
      (ref.cast (ref $B)
        ;; This local.tee should not stop us from optimizing.
        (local.tee $local
          (local.get $x)
        )
      )
    )
  )

  ;; CHECK:      (func $caller (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (local $x (ref null $A))
    (call $called
      (local.tee $x
        (ref.cast (ref $A) ;; this cast will be refined
          (local.get $any)
        )
      )
    )
  )
)

;; As above, but now add some control flow before the cast in the function.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $1 (func (param (ref null $A))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $1) (param $x (ref null $A))
  ;; CHECK-NEXT:  (local $local (ref null $A))
  ;; CHECK-NEXT:  (if
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (then
  ;; CHECK-NEXT:    (return)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (local $local (ref null $A))
    ;; Control flow before the cast *does* stop us from optimizing.
    (if
      (i32.const 0)
      (then
        (return)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (local $x (ref null $A))
    (call $called
      (local.tee $x
        (ref.cast (ref $A) ;; this cast will *not* be refined
          (local.get $any)
        )
      )
    )
  )
)

;; As above, but make the cast uninteresting so we do not optimize.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  (type $B (sub $A (struct (field (mut i32)))))

  ;; CHECK:      (type $1 (func (param (ref null $A))))

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $1) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (drop
      (ref.cast (ref $A) ;; This cast only removes nullability.
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (local $x (ref null $A))
    (call $called
      (local.tee $x
        (ref.cast (ref $A)
                     ;; This cast will *not* be refined, as it is already non-
                     ;; nullable here, and the other cast did not improve the
                     ;; heap type.
          (local.get $any)
        )
      )
    )
  )
)

;; As above, but two casts in the called function.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

  ;; CHECK:      (type $C (sub $B (struct (field (mut i32)))))
  (type $C (sub $B (struct (field (mut i32)))))

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $2) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $C)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    ;; Two casts. We keep the first, which is simple to do, and good enough in
    ;; the general case as other optimizations will leave the most-refined one.
    ;; (But in this test, it is less optimal actually.)
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (ref.cast (ref $C)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $any anyref)
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $x
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (local $x (ref null $A))
    (call $called
      (local.tee $x
        (ref.cast (ref $A) ;; this cast will be refined to $B.
          (local.get $any)
        )
      )
    )
  )
)

;; Multiple parameters with control flow between them.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $2) (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $z)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
    ;; All parameters are cast.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $y)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $z)
      )
    )
  )

  ;; CHECK:      (func $caller (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (block (result (ref $A))
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (block (result (ref $B))
  ;; CHECK-NEXT:    (if
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (return)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (block (result (ref $B))
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    ;; All three params get similar blocks+casts, but the middle one might
    ;; transfer control flow, so we cannot optimize the first parameter (we
    ;; might not reach the call, so we can't assume the call's cast suceeds).
    ;; As a result, only the last two casts are refined to $B, but not the
    ;; first.
    (call $called
      (block (result (ref $A))
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
      (block (result (ref $A))
        (if
          (i32.const 0)
          (then
            (return)
          )
        )
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
      (block (result (ref $A))
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
    )
  )
)

;; As above, but without the cast in the middle of the called function.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $2) (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (local.get $y)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $z)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      ;; This changed to not have a cast.
      (local.get $y)
    )
    (drop
      (ref.cast (ref $B)
        (local.get $z)
      )
    )
  )

  ;; CHECK:      (func $caller (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (block (result (ref $A))
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (block (result (ref $A))
  ;; CHECK-NEXT:    (if
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (return)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (block (result (ref $B))
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    ;; We can't refine the first cast because of control flow, like before, and
    ;; we can't refine the middle because the called function has no cast, but
    ;; we can still refine the last one.
    (call $called
      (block (result (ref $A))
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
      (block (result (ref $A))
        (if
          (i32.const 0)
          (then
            (return)
          )
        )
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
      (block (result (ref $A))
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
    )
  )
)

;; As above, but with a different control flow transfer in the caller, a call.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

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

  ;; CHECK:      (import "a" "b" (func $get-any (type $2) (result anyref)))
  (import "a" "b" (func $get-any (result anyref)))

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $3) (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $z)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
    ;; All parameters are cast.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $y)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $z)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (call $get-any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (call $called
      (ref.cast (ref $A)
        (local.get $any)
      )
      (ref.cast (ref $A)
        ;; This call might transfer control flow (if it throws), so we
        ;; can't optimize before it, but the last two casts will become $B.
        (call $get-any)
      )
      (ref.cast (ref $A)
        (local.get $any)
      )
    )
  )
)

;; As above, but with yet another control flow transfer, using an if.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

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

  ;; CHECK:      (import "a" "b" (func $get-any (type $2) (result anyref)))
  (import "a" "b" (func $get-any (result anyref)))

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $3) (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $y)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $z)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A)) (param $y (ref null $A)) (param $z (ref null $A))
    ;; All parameters are cast.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $y)
      )
    )
    (drop
      (ref.cast (ref $B)
        (local.get $z)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (if (result (ref $A))
  ;; CHECK-NEXT:    (i32.const 0)
  ;; CHECK-NEXT:    (then
  ;; CHECK-NEXT:     (return)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (else
  ;; CHECK-NEXT:     (ref.cast (ref $A)
  ;; CHECK-NEXT:      (local.get $any)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (call $called
      (ref.cast (ref $A)
        (local.get $any)
      )
      ;; One if arm transfers control flow, so while we have a fallthrough
      ;; value we cannot optimize it (in fact, it might never be reached, at
      ;; least if the constant were not 0). As a result we'll optimize only the
      ;; very last cast.
      (if (result (ref $A))
        (i32.const 0)
        (then
          (return)
        )
        (else
          (ref.cast (ref $A)
            (local.get $any)
          )
        )
      )
      (ref.cast (ref $A)
        (local.get $any)
      )
    )
  )
)

;; A cast that will fail.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $2) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref (exact $B))
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $3)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out")
    (call $called
      ;; The called function will cast to $B, but this is an $A, so the cast
      ;; will fail. We can infer that the code here is unreachable. Note that no
      ;; ref.cast appears here - we infer this even without seeing a cast.
      (struct.new $A
        (i32.const 10)
      )
    )
    ;; Another call that will not be modified.
    (call $called
      (struct.new $B
        (i32.const 20)
      )
    )
  )
)

;; Test that we refine using ref.as_non_null and not just ref.cast.
(module
  ;; CHECK:      (type $A (struct (field (mut i32))))
  (type $A (struct (field (mut i32))))

  ;; CHECK:      (type $1 (func (param (ref null $A))))

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $1) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.as_non_null
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (drop
      (ref.as_non_null
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    (call $called
      ;; This cast can become non-nullable.
      (ref.cast (ref null $A)
        (local.get $any)
      )
    )
  )
)

;; Verify we do not propagate *less*-refined information.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

  ;; CHECK:      (type $C (sub $B (struct (field (mut i32)))))
  (type $C (sub $B (struct (field (mut i32)))))

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

  ;; CHECK:      (type $4 (func (param (ref null $A))))

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

  ;; CHECK:      (export "caller-C" (func $caller-C))

  ;; CHECK:      (export "caller-B" (func $caller-B))

  ;; CHECK:      (export "caller-A" (func $caller-A))

  ;; CHECK:      (func $called (type $4) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    ;; This function casts the A to a B.
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $maker (type $5)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $A
  ;; CHECK-NEXT:    (i32.const 10)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $B
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new $C
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $maker
    ;; A always contains 10, and B 20, and C 30.
    (drop
      (struct.new $A
        (i32.const 10)
      )
    )
    (drop
      (struct.new $B
        (i32.const 20)
      )
    )
    (drop
      (struct.new $C
        (i32.const 30)
      )
    )
  )

  ;; CHECK:      (func $caller-C (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (local $temp-C (ref $C))
  ;; CHECK-NEXT:  (local $temp-any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $temp-C
  ;; CHECK-NEXT:    (ref.cast (ref $C)
  ;; CHECK-NEXT:     (local.tee $temp-any
  ;; CHECK-NEXT:      (local.get $any)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 30)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $B 0
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $temp-any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (ref.cast (ref $A)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller-C (export "caller-C") (param $any anyref)
    (local $temp-C (ref $C))
    (local $temp-any anyref)
    (call $called
      (local.tee $temp-C
        (ref.cast (ref $C)
                     ;; This cast is already more refined than even the called
                     ;; function casts to. It should stay as it is.
          (local.tee $temp-any
            (local.get $any)
          )
        )
      )
    )
    (drop
      (struct.get $A 0 ;; the reference contains a C, so this value is 30.
        (ref.cast (ref $A)
          (local.get $temp-C)
        )
      )
    )
    (drop
      (struct.get $A 0 ;; We infer that $temp-any is $B from the cast in the
                       ;; call, so the cast here can be improved, but not the
                       ;; value (which can be 20 or 30).
                       ;; TODO: We can infer from the ref.cast $C in this
                       ;;       function backwards into the tee and its value.
        (ref.cast (ref $A)
          (local.get $temp-any)
        )
      )
    )
    (drop
      (struct.get $A 0 ;; We have not inferred anything about the param, so this
                       ;; is not optimized yet, but it could be. TODO
        (ref.cast (ref $A)
          (local.get $any)
        )
      )
    )
  )

  ;; CHECK:      (func $caller-B (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (local $temp (ref $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $temp
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $temp)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller-B (export "caller-B") (param $any anyref)
    (local $temp (ref $A))
    (call $called
      (local.tee $temp
        (ref.cast (ref $B) ;; This cast is equal to the called cast. It should remain.
          (local.get $any)
        )
      )
    )
    (drop
      (struct.get $A 0 ;; Nothing can be inferred here.
        (local.get $temp)
      )
    )
  )

  ;; CHECK:      (func $caller-A (type $3) (param $any anyref)
  ;; CHECK-NEXT:  (local $temp (ref $A))
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (local.tee $temp
  ;; CHECK-NEXT:    (ref.cast (ref $B)
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $A 0
  ;; CHECK-NEXT:    (local.get $temp)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller-A (export "caller-A") (param $any anyref)
    (local $temp (ref $A))
    (call $called
      (local.tee $temp
        (ref.cast (ref $A) ;; This cast is less refined, and can be improved to B.
          (local.get $any)
        )
      )
    )
    (drop
      (struct.get $A 0 ;; Nothing can be inferred here.
        (local.get $temp)
      )
    )
  )
)

;; Refine a type to unreachable. B1 and B2 are sibling subtypes of A, and the
;; caller passes in a B1 that is cast in the function to B2.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $B1 (sub $A (struct (field (mut i32)))))
    (type $B1 (sub $A (struct (field (mut i32)))))

    ;; CHECK:       (type $B2 (sub $A (struct (field (mut i32)))))
    (type $B2 (sub $A (struct (field (mut i32)))))

    ;; CHECK:       (type $C1 (sub $B1 (struct (field (mut i32)))))
    (type $C1 (sub $B1 (struct (field (mut i32)))))
  )

  ;; CHECK:      (type $4 (func (param (ref null $A))))

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

  ;; CHECK:      (export "caller" (func $caller))

  ;; CHECK:      (func $called (type $4) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B1)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (drop
      (ref.cast (ref $B1)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $5) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (struct.new $B1
  ;; CHECK-NEXT:    (i32.const 20)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (struct.new $C1
  ;; CHECK-NEXT:    (i32.const 30)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "caller") (param $any anyref)
    ;; The cast of this A to B1 will fail, so it is unreachable.
    (call $called
      (struct.new $A
        (i32.const 10)
      )
    )
    ;; This cast of B1 to itself will succeed, so nothing changes.
    (call $called
      (struct.new $B1
        (i32.const 20)
      )
    )
    ;; The cast of this C1 to its supertype B1 will succeed.
    (call $called
      (struct.new $C1
        (i32.const 30)
      )
    )
    ;; Casting B2 to B1 will fail as they are sibling types.
    (call $called
      (struct.new $B2
        (i32.const 40)
      )
    )
  )
)

;; Check we ignore casts of non-param locals.
(module
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  (type $B (sub $A (struct (field (mut i32)))))

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $0)
  ;; CHECK-NEXT:  (local $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.null none)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called
    (local $x (ref null $A))
    ;; This casts a local in the entry block, but it is not a parameter, so we
    ;; should ignore it and not error.
    (drop
      (ref.cast (ref null $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $0)
  ;; CHECK-NEXT:  (call $called)
  ;; CHECK-NEXT: )
  (func $caller (export "out")
    (call $called)
  )
)

;; Check all combinations of types passed to a nullable cast.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

  ;; CHECK:      (type $C (sub $B (struct (field (mut i32)))))
  (type $C (sub $B (struct (field (mut i32)))))

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $3) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref null $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (drop
      (ref.cast (ref null $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $x anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref null $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref null $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $C)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref null $C)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $x anyref)
    ;; A non-nullable A passed into a cast of B means this value must be a non-
    ;; nullable B.
    (call $called
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; This will be refined to (nullable) B.
    (call $called
      (ref.cast (ref null $A)
        (local.get $x)
      )
    )
    ;; Casts of B remain the same.
    (call $called
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (call $called
      (ref.cast (ref null $B)
        (local.get $x)
      )
    )
    ;; Casts of C remain the same.
    (call $called
      (ref.cast (ref $C)
        (local.get $x)
      )
    )
    (call $called
      (ref.cast (ref null $C)
        (local.get $x)
      )
    )
  )
)

;; Check all combinations of types passed to a *non*-nullable cast.
(module
  ;; CHECK:      (type $A (sub (struct (field (mut i32)))))
  (type $A (sub (struct (field (mut i32)))))

  ;; CHECK:      (type $B (sub $A (struct (field (mut i32)))))
  (type $B (sub $A (struct (field (mut i32)))))

  ;; CHECK:      (type $C (sub $B (struct (field (mut i32)))))
  (type $C (sub $B (struct (field (mut i32)))))

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

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $3) (param $x (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x (ref null $A))
    (drop
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $x anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $C)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $C)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $x anyref)
    ;; Casts of A are refined.
    (call $called
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
    ;; This will be refined to B.
    (call $called
      (ref.cast (ref null $A)
        (local.get $x)
      )
    )
    ;; Casts of B turn or stay as non-nullable B.
    (call $called
      (ref.cast (ref $B)
        (local.get $x)
      )
    )
    (call $called
      (ref.cast (ref null $B)
        (local.get $x)
      )
    )
    ;; This cast of C remains the same.
    (call $called
      (ref.cast (ref $C)
        (local.get $x)
      )
    )
    ;; A nullable C passed into a cast of non-null B means this value must be a
    ;; non-nullable C.
    (call $called
      (ref.cast (ref null $C)
        (local.get $x)
      )
    )
  )
)

;; A cast of an array.
(module
  ;; CHECK:      (type $0 (func (param anyref)))

  ;; CHECK:      (type $A (array (mut i32)))
  (type $A (array (mut i32)))

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $0) (param $x anyref)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $x anyref)
    (drop
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )

  ;; CHECK:      (func $caller (type $0) (param $x anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $x anyref)
    (call $called
      ;; This will be refined to a non-nullable cast.
      (ref.cast (ref null $A)
        (local.get $x)
      )
    )
  )
)

;; Test array and struct operations.
(module
  ;; CHECK:      (type $B (array (mut i32)))

  ;; CHECK:      (type $A (struct (field (mut i32))))
  (type $A (struct (field (mut i32))))

  (type $B (array (mut i32)))

  ;; CHECK:      (type $C (array (mut funcref)))
  (type $C (array (mut funcref)))


  ;; CHECK:      (type $3 (func (param (ref null $A) (ref null $A) (ref null $B) (ref null $B) (ref null $B) (ref null $B) (ref null $B) (ref null $B) (ref null $B) (ref null $C) (ref null $A))))

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

  ;; CHECK:      (data $d "a")
  (data $d "a")

  ;; CHECK:      (elem $e func)
  (elem $e funcref)

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $called (type $3) (param $struct.get (ref null $A)) (param $struct.set (ref null $A)) (param $array.get (ref null $B)) (param $array.set (ref null $B)) (param $array.len (ref null $B)) (param $array.copy.src (ref null $B)) (param $array.copy.dest (ref null $B)) (param $array.fill (ref null $B)) (param $array.init_data (ref null $B)) (param $array.init_elem (ref null $C)) (param $ref.test (ref null $A))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.set $A 0
  ;; CHECK-NEXT:   (local.get $struct.set)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (array.get $B
  ;; CHECK-NEXT:    (local.get $array.get)
  ;; CHECK-NEXT:    (i32.const 1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (array.set $B
  ;; CHECK-NEXT:   (local.get $array.set)
  ;; CHECK-NEXT:   (i32.const 2)
  ;; CHECK-NEXT:   (i32.const 3)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (array.len
  ;; CHECK-NEXT:    (local.get $array.len)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (array.copy $B $B
  ;; CHECK-NEXT:   (local.get $array.copy.src)
  ;; CHECK-NEXT:   (i32.const 4)
  ;; CHECK-NEXT:   (local.get $array.copy.dest)
  ;; CHECK-NEXT:   (i32.const 5)
  ;; CHECK-NEXT:   (i32.const 6)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (array.fill $B
  ;; CHECK-NEXT:   (local.get $array.fill)
  ;; CHECK-NEXT:   (i32.const 7)
  ;; CHECK-NEXT:   (i32.const 8)
  ;; CHECK-NEXT:   (i32.const 9)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (array.init_data $B $d
  ;; CHECK-NEXT:   (local.get $array.init_data)
  ;; CHECK-NEXT:   (i32.const 10)
  ;; CHECK-NEXT:   (i32.const 11)
  ;; CHECK-NEXT:   (i32.const 12)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (array.init_elem $C $e
  ;; CHECK-NEXT:   (local.get $array.init_elem)
  ;; CHECK-NEXT:   (i32.const 13)
  ;; CHECK-NEXT:   (i32.const 14)
  ;; CHECK-NEXT:   (i32.const 15)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.test (ref $A)
  ;; CHECK-NEXT:    (local.get $ref.test)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called
    (param $struct.get (ref null $A))
    (param $struct.set (ref null $A))
    (param $array.get (ref null $B))
    (param $array.set (ref null $B))
    (param $array.len (ref null $B))
    (param $array.copy.src (ref null $B))
    (param $array.copy.dest (ref null $B))
    (param $array.fill (ref null $B))
    (param $array.init_data (ref null $B))
    (param $array.init_elem (ref null $C))
    (param $ref.test (ref null $A))

    ;; All the operations trap on null, aside from ref.test.
    (drop
      (struct.get $A 0
        (local.get $struct.get)
      )
    )
    (struct.set $A 0
      (local.get $struct.set)
      (i32.const 0)
    )
    (drop
      (array.get $B
        (local.get $array.get)
        (i32.const 1)
      )
    )
    (array.set $B
      (local.get $array.set)
      (i32.const 2)
      (i32.const 3)
    )
    (drop
      (array.len
        (local.get $array.len)
      )
    )
    (array.copy $B $B
      (local.get $array.copy.src)
      (i32.const 4)
      (local.get $array.copy.dest)
      (i32.const 5)
      (i32.const 6)
    )
    (array.fill $B
      (local.get $array.fill)
      (i32.const 7)
      (i32.const 8)
      (i32.const 9)
    )
    (array.init_data $B $d
      (local.get $array.init_data)
      (i32.const 10)
      (i32.const 11)
      (i32.const 12)
    )
    (array.init_elem $C $e
      (local.get $array.init_elem)
      (i32.const 13)
      (i32.const 14)
      (i32.const 15)
    )
    (drop
      (ref.test (ref $A)
        (local.get $ref.test)
      )
    )
  )

  ;; CHECK:      (func $caller (type $4) (param $any anyref)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref $C)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (ref.cast (ref null $A)
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $any anyref)
    ;; All these casts will be refined to non-nullable, aside from the last
    ;; param which is but a ref.test.
    (call $called
      (ref.cast (ref null $A)
        (local.get $any)
      )
      (ref.cast (ref null $A)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $B)
        (local.get $any)
      )
      (ref.cast (ref null $C)
        (local.get $any)
      )
      (ref.cast (ref null $A)
        (local.get $any)
      )
    )
  )
)

;; A CallRef with no non-trapping targets must be unreachable if traps never
;; happen. However, this requires closed world, so we do nothing here. (This
;; test is mirrored in gufa-tnh-closed, where closed world *is* enabled.)
(module
  ;; CHECK:      (type $A (func))
  (type $A (func))

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

  ;; CHECK:      (export "out" (func $caller))

  ;; CHECK:      (func $impossible (type $A)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $impossible (type $A)
    ;; This cannot be called if traps never happen.
    (unreachable)
  )

  ;; CHECK:      (func $caller (type $1) (param $x funcref)
  ;; CHECK-NEXT:  (call_ref $A
  ;; CHECK-NEXT:   (ref.cast (ref $A)
  ;; CHECK-NEXT:    (local.get $x)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "out") (param $x funcref)
    ;; In closed world this call must trap, as the only function of the right
    ;; type will trap. But we are in open world so we do not optimize here.
    ;; TODO: We could analyze publicly visible tables, exports, etc. to see
    ;;       whether any more functions could be possible even in an open world.
    (call_ref $A
      (ref.cast (ref $A)
        (local.get $x)
      )
    )
  )
)

;; Control flow around calls.
(module
  ;; CHECK:      (type $A (sub (struct)))
  (type $A (sub (struct)))

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

  ;; CHECK:      (type $B (sub $A (struct)))
  (type $B (sub $A (struct)))

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

  ;; CHECK:      (import "a" "b" (func $import-throw (type $1)))
  (import "a" "b" (func $import-throw))

  ;; CHECK:      (export "a" (func $caller))

  ;; CHECK:      (func $called (type $3) (param $0 (ref null $A))
  ;; CHECK-NEXT:  (call $import-throw)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref $B)
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $called (param $0 (ref null $A))
    ;; This function calls an import, and later casts. We cannot use those casts
    ;; to infer anything in the callers, since the import might throw (in which
    ;; case we'd never reach the cast).
    (call $import-throw)
    (drop
      (ref.cast (ref $B)
        (local.get $0)
      )
    )
  )

  ;; CHECK:      (func $caller (type $1)
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (struct.new_default $B)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call $called
  ;; CHECK-NEXT:   (struct.new_default $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (export "a")
    ;; This call sends a $B which will be cast to $B (assuming the import does
    ;; not trap), so nothing should happen here.
    (call $called
      (struct.new $B)
    )
    ;; This call sends an $A, which would fail the cast if it were reached. But
    ;; it might not, so we do nothing here.
    (call $called
      (struct.new $A)
    )
  )
)

(module
 ;; CHECK:      (type $0 (func (param i32) (result (ref null (shared any)))))

 ;; CHECK:      (table $0 3 3 (ref null (shared any)))
 (table $0 3 3 (ref null (shared any)))
 ;; CHECK:      (elem $0 (table $0) (i32.const 0) (ref null (shared i31)) (item (ref.i31_shared
 ;; CHECK-NEXT:  (i32.const 999)
 ;; CHECK-NEXT: )))
 (elem $0 (table $0) (i32.const 0) (ref null (shared i31)) (item (ref.i31_shared (i32.const 999))))
 ;; CHECK:      (export "get" (func $0))

 ;; CHECK:      (func $0 (type $0) (param $0 i32) (result (ref null (shared any)))
 ;; CHECK-NEXT:  (ref.cast (ref null (shared i31))
 ;; CHECK-NEXT:   (table.get $0
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $0 (export "get") (param $0 i32) (result (ref null (shared any)))
  ;; Regression test for a bug where subtypes.h did not handle shared types
  ;; correctly, causing this example to be misoptimized to return null.
  (ref.cast (ref null (shared i31))
   (table.get $0
    (i32.const 0)
   )
  )
 )
)

;; Test writes to locals that interfere with inferences about casts.
(module
 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $top (sub (struct)))
  (type $top (sub (struct)))
  ;; CHECK:       (type $bot (sub $top (struct)))
  (type $bot (sub $top (struct)))
 )

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

 ;; CHECK:      (type $3 (func (param (ref extern))))

 ;; CHECK:      (export "$invokeMain" (func $invokeMain))

 ;; CHECK:      (func $main-set (type $2) (param $0 (ref $top))
 ;; CHECK-NEXT:  (local.set $0
 ;; CHECK-NEXT:   (struct.new_default $bot)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (ref.cast (ref (exact $bot))
 ;; CHECK-NEXT:    (local.get $0)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $main-set (param (ref $top))
  ;; We receive a top as input, but write a bot to it, trampling the ignored
  ;; parameter. Thanks to the trampling, the cast below will succeed, and so we
  ;; should not make anything unreachable in the caller - nothing traps here.
  (local.set 0
   (struct.new $bot)
  )
  (drop
   (ref.cast (ref $bot)
    (local.get 0)
   )
  )
 )

 ;; CHECK:      (func $main-noset (type $2) (param $0 (ref $top))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $main-noset (param (ref $top))
  ;; As above, but without the local.set. Here we will trap, so the caller can
  ;; optimize to unreachable.
  (drop
   (ref.cast (ref $bot)
    (local.get 0)
   )
  )
 )

 ;; CHECK:      (func $invokeMain (type $3) (param $0 (ref extern))
 ;; CHECK-NEXT:  (call $main-set
 ;; CHECK-NEXT:   (struct.new_default $top)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (call $main-noset
 ;; CHECK-NEXT:   (unreachable)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $invokeMain (export "$invokeMain") (param (ref extern))
  (call $main-set
   (struct.new $top)
  )
  (call $main-noset
   (struct.new $top)
  )
 )
)
