;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.
;; RUN: wasm-opt %s --optimize-instructions --traps-never-happen -all -S -o - | filecheck %s --check-prefix TNH
;; RUN: wasm-opt %s --optimize-instructions                      -all -S -o - | filecheck %s --check-prefix NO_TNH

(module
  ;; TNH:      (type $struct (sub (struct (field (mut i32)))))
  ;; NO_TNH:      (type $struct (sub (struct (field (mut i32)))))
  (type $struct (sub (struct (field (mut i32)))))

  ;; TNH:      (type $void (func))
  ;; NO_TNH:      (type $void (func))
  (type $void (func))

  ;; TNH:      (import "a" "b" (func $import (type $2) (result i32)))
  ;; NO_TNH:      (import "a" "b" (func $import (type $2) (result i32)))
  (import "a" "b" (func $import (result i32)))

  ;; TNH:      (func $ref.eq (type $7) (param $a eqref) (param $b eqref) (result i32)
  ;; TNH-NEXT:  (ref.eq
  ;; TNH-NEXT:   (local.get $a)
  ;; TNH-NEXT:   (local.get $b)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $ref.eq (type $7) (param $a eqref) (param $b eqref) (result i32)
  ;; NO_TNH-NEXT:  (ref.eq
  ;; NO_TNH-NEXT:   (ref.cast (ref $struct)
  ;; NO_TNH-NEXT:    (local.get $a)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (ref.cast (ref struct)
  ;; NO_TNH-NEXT:    (local.get $b)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $ref.eq (param $a (ref null eq)) (param $b (ref null eq)) (result i32)
    ;; When traps never happen we can remove all the casts here, since they do
    ;; not affect the comparison of the references.
    (ref.eq
      ;; When traps can happen we can still improve this by removing and
      ;; combining redundant casts.
      (ref.cast (ref struct)
        (ref.as_non_null
          (ref.cast (ref null $struct)
            (local.get $a)
          )
        )
      )
      ;; Note that we can remove the non-null casts here in both modes, as the
      ;; ref.cast struct also checks for null.
      (ref.cast (ref struct)
        (ref.as_non_null
          (ref.as_non_null
            (local.get $b)
          )
        )
      )
    )
  )

  ;; TNH:      (func $ref.eq-no (type $8) (param $a eqref) (param $b eqref) (param $any anyref)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $ref.eq-no (type $8) (param $a eqref) (param $b eqref) (param $any anyref)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.eq
  ;; NO_TNH-NEXT:    (ref.cast (ref null $struct)
  ;; NO_TNH-NEXT:     (local.get $any)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (ref.cast (ref struct)
  ;; NO_TNH-NEXT:     (local.get $any)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $ref.eq-no (param $a (ref null eq)) (param $b (ref null eq)) (param $any anyref)
    ;; We must leave the inputs to ref.eq of type eqref or a subtype.
    (drop
      (ref.eq
        (ref.cast (ref null $struct)
          (local.get $any) ;; *Not* an eqref!
        )
        (ref.as_non_null
          (ref.cast (ref struct)
            (ref.as_non_null
              (local.get $any) ;; *Not* an eqref!
            )
          )
        )
      )
    )
  )

  ;; TNH:      (func $ref.is (type $3) (param $a eqref) (result i32)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (ref.cast (ref $struct)
  ;; TNH-NEXT:    (local.get $a)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (i32.const 0)
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $ref.is (type $3) (param $a eqref) (result i32)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.cast (ref $struct)
  ;; NO_TNH-NEXT:    (local.get $a)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (i32.const 0)
  ;; NO_TNH-NEXT: )
  (func $ref.is (param $a (ref null eq)) (result i32)
    ;; In this case non-nullability is enough to tell that the ref.is will
    ;; return 0. TNH does not help here.
    (ref.is_null
      (ref.cast (ref $struct)
        (ref.as_non_null
          (ref.cast (ref struct)
            (local.get $a)
          )
        )
      )
    )
  )

  ;; TNH:      (func $ref.is_b (type $9) (param $a eqref) (param $f funcref) (result i32)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (ref.is_null
  ;; TNH-NEXT:    (local.get $a)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (ref.is_null
  ;; TNH-NEXT:   (local.get $f)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $ref.is_b (type $9) (param $a eqref) (param $f funcref) (result i32)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.is_null
  ;; NO_TNH-NEXT:    (ref.cast (ref null $struct)
  ;; NO_TNH-NEXT:     (local.get $a)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (ref.is_null
  ;; NO_TNH-NEXT:   (ref.cast (ref null $void)
  ;; NO_TNH-NEXT:    (local.get $f)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $ref.is_b (param $a eqref) (param $f funcref) (result i32)
    ;; Here we only have a cast, and no cast operations that force the value
    ;; to be non-nullable. That means we cannot remove the ref.is, but we can
    ;; remove the cast in TNH.
    (drop
      (ref.is_null
        (ref.cast (ref null $struct)
          (local.get $a)
        )
      )
    )
    ;; It works on func references, too.
    (ref.is_null
      (ref.cast (ref null $void)
        (local.get $f)
      )
    )
  )

  ;; TNH:      (func $ref.test (type $3) (param $a eqref) (result i32)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block (result i32)
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (ref.cast i31ref
  ;; TNH-NEXT:      (local.get $a)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (i32.const 1)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (block (result i32)
  ;; TNH-NEXT:   (drop
  ;; TNH-NEXT:    (ref.as_non_null
  ;; TNH-NEXT:     (local.get $a)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $ref.test (type $3) (param $a eqref) (result i32)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result i32)
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (ref.cast i31ref
  ;; NO_TNH-NEXT:      (local.get $a)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (block (result i32)
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (ref.as_non_null
  ;; NO_TNH-NEXT:     (local.get $a)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $ref.test (param $a eqref) (result i32)
    (drop
      (ref.test i31ref
        (ref.cast i31ref
          (local.get $a)
        )
      )
    )
    (ref.test (ref eq)
      (ref.cast (ref eq)
        (local.get $a)
      )
    )
  )

  ;; TNH:      (func $if.arm.null (type $4) (param $x i32) (param $ref (ref $struct))
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (block (result (ref $struct))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (local.get $x)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (local.get $ref)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (block (result (ref $struct))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (local.get $x)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (local.get $ref)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 2)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $if.arm.null (type $4) (param $x i32) (param $ref (ref $struct))
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (if (result (ref null $struct))
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:    (then
  ;; NO_TNH-NEXT:     (local.get $ref)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (else
  ;; NO_TNH-NEXT:     (ref.null none)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (if (result (ref null $struct))
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:    (then
  ;; NO_TNH-NEXT:     (ref.null none)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (else
  ;; NO_TNH-NEXT:     (local.get $ref)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 2)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $if.arm.null (param $x i32) (param $ref (ref $struct))
    ;; A set will trap on a null, so in tnh mode we know the null arm is not
    ;; executed, and the other one is.
    (struct.set $struct 0
      (if (result (ref null $struct))
        (local.get $x)
        (then
          (local.get $ref)
        )
        (else
          (ref.null none)
        )
      )
      (i32.const 1)
    )
    (struct.set $struct 0
      (if (result (ref null $struct))
        (local.get $x)
        (then
          (ref.null none)
        )
        (else
          (local.get $ref)
        )
      )
      (i32.const 2)
    )
  )

  ;; TNH:      (func $select.arm.null (type $4) (param $x i32) (param $ref (ref $struct))
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (block (result (ref $struct))
  ;; TNH-NEXT:    (block
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (ref.null none)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (local.get $x)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (local.get $ref)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (block (result (ref $struct))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (ref.null none)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (block (result (ref $struct))
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (local.get $x)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:     (local.get $ref)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 2)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $select.arm.null (type $4) (param $x i32) (param $ref (ref $struct))
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (select (result (ref null $struct))
  ;; NO_TNH-NEXT:    (local.get $ref)
  ;; NO_TNH-NEXT:    (ref.null none)
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (select (result (ref null $struct))
  ;; NO_TNH-NEXT:    (ref.null none)
  ;; NO_TNH-NEXT:    (local.get $ref)
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 2)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $select.arm.null (param $x i32) (param $ref (ref $struct))
    ;; As above but with a select.
    (struct.set $struct 0
      (select (result (ref null $struct))
        (local.get $ref)
        (ref.null none)
        (local.get $x)
      )
      (i32.const 1)
    )
    (struct.set $struct 0
      (select (result (ref null $struct))
        (ref.null none)
        (local.get $ref)
        (local.get $x)
      )
      (i32.const 2)
    )
  )

  ;; TNH:      (func $select.arm.null.effects (type $void)
  ;; TNH-NEXT:  (local $temp i32)
  ;; TNH-NEXT:  (local $1 (ref $struct))
  ;; TNH-NEXT:  (local $2 (ref $struct))
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (block (result (ref $struct))
  ;; TNH-NEXT:    (local.set $1
  ;; TNH-NEXT:     (struct.new $struct
  ;; TNH-NEXT:      (local.tee $temp
  ;; TNH-NEXT:       (i32.const 1)
  ;; TNH-NEXT:      )
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (block
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (block (result nullref)
  ;; TNH-NEXT:       (local.set $temp
  ;; TNH-NEXT:        (i32.const 2)
  ;; TNH-NEXT:       )
  ;; TNH-NEXT:       (ref.null none)
  ;; TNH-NEXT:      )
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (local.get $temp)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (local.get $1)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (block (result (ref $struct))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (block (result nullref)
  ;; TNH-NEXT:      (local.set $temp
  ;; TNH-NEXT:       (i32.const 2)
  ;; TNH-NEXT:      )
  ;; TNH-NEXT:      (ref.null none)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (block (result (ref $struct))
  ;; TNH-NEXT:     (local.set $2
  ;; TNH-NEXT:      (struct.new $struct
  ;; TNH-NEXT:       (local.tee $temp
  ;; TNH-NEXT:        (i32.const 1)
  ;; TNH-NEXT:       )
  ;; TNH-NEXT:      )
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (local.get $temp)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:     (local.get $2)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 2)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $select.arm.null.effects (type $void)
  ;; NO_TNH-NEXT:  (local $temp i32)
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (select (result (ref null $struct))
  ;; NO_TNH-NEXT:    (struct.new $struct
  ;; NO_TNH-NEXT:     (local.tee $temp
  ;; NO_TNH-NEXT:      (i32.const 1)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (block (result nullref)
  ;; NO_TNH-NEXT:     (local.set $temp
  ;; NO_TNH-NEXT:      (i32.const 2)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (ref.null none)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (local.get $temp)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (select (result (ref null $struct))
  ;; NO_TNH-NEXT:    (block (result nullref)
  ;; NO_TNH-NEXT:     (local.set $temp
  ;; NO_TNH-NEXT:      (i32.const 2)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (ref.null none)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (struct.new $struct
  ;; NO_TNH-NEXT:     (local.tee $temp
  ;; NO_TNH-NEXT:      (i32.const 1)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (local.get $temp)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 2)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $select.arm.null.effects
    (local $temp i32)
    ;; As above but there are conflicting effects and we must add a local when
    ;; we optimize.
    (struct.set $struct 0
      (select (result (ref null $struct))
        (struct.new $struct
          (local.tee $temp
            (i32.const 1)
          )
        )
        (block (result (ref null none))
          (local.set $temp
            (i32.const 2)
          )
          (ref.null none)
        )
        (local.get $temp)
      )
      (i32.const 1)
    )
    (struct.set $struct 0
      (select (result (ref null $struct))
        (block (result (ref null none))
          (local.set $temp
            (i32.const 2)
          )
          (ref.null none)
        )
        (struct.new $struct
          (local.tee $temp
            (i32.const 1)
          )
        )
        (local.get $temp)
      )
      (i32.const 2)
    )
  )

  ;; TNH:      (func $null.arm.null.effects (type $void)
  ;; TNH-NEXT:  (block
  ;; TNH-NEXT:   (drop
  ;; TNH-NEXT:    (select
  ;; TNH-NEXT:     (unreachable)
  ;; TNH-NEXT:     (ref.null none)
  ;; TNH-NEXT:     (call $get-i32)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (unreachable)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (block
  ;; TNH-NEXT:   (drop
  ;; TNH-NEXT:    (block (result nullref)
  ;; TNH-NEXT:     (drop
  ;; TNH-NEXT:      (call $get-i32)
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:     (ref.null none)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (unreachable)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $null.arm.null.effects (type $void)
  ;; NO_TNH-NEXT:  (block
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (select
  ;; NO_TNH-NEXT:     (unreachable)
  ;; NO_TNH-NEXT:     (ref.null none)
  ;; NO_TNH-NEXT:     (call $get-i32)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (block
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (block (result nullref)
  ;; NO_TNH-NEXT:     (drop
  ;; NO_TNH-NEXT:      (call $get-i32)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (ref.null none)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $null.arm.null.effects
    ;; Verify we do not error on a null reference in a select, even if cast to
    ;; non-null.
    (struct.set $struct 0
      (select (result (ref null $struct))
        (ref.as_non_null
          (ref.null none)
        )
        (ref.null none)
        (call $get-i32)
      )
      (i32.const 1)
    )
    ;; The same, but without ref.as_non_null.
    (struct.set $struct 0
      (select (result (ref null $struct))
        (ref.null none)
        (ref.null none)
        (call $get-i32)
      )
      (i32.const 1)
    )
  )

  ;; TNH:      (func $set-get-cast (type $10) (param $ref structref)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (struct.get $struct 0
  ;; TNH-NEXT:    (ref.cast (ref $struct)
  ;; TNH-NEXT:     (local.get $ref)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (ref.cast (ref $struct)
  ;; TNH-NEXT:    (local.get $ref)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (ref.cast (ref null $struct)
  ;; TNH-NEXT:    (local.get $ref)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (block (result i32)
  ;; TNH-NEXT:    (return)
  ;; TNH-NEXT:    (i32.const 1)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $set-get-cast (type $10) (param $ref structref)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (struct.get $struct 0
  ;; NO_TNH-NEXT:    (ref.cast (ref $struct)
  ;; NO_TNH-NEXT:     (local.get $ref)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (ref.cast (ref null $struct)
  ;; NO_TNH-NEXT:    (local.get $ref)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (ref.cast (ref null $struct)
  ;; NO_TNH-NEXT:    (local.get $ref)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (block (result i32)
  ;; NO_TNH-NEXT:    (return)
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $set-get-cast (param $ref (ref null struct))
    ;; A nullable cast flowing into a place that traps on null can become a
    ;; non-nullable cast.
    (drop
      (struct.get $struct 0
        (ref.cast (ref null $struct)
          (local.get $ref)
        )
      )
    )
    ;; Ditto for a set, at least in traps-happen mode.
    ;; TODO handle non-TNH as well, but we need to be careful of effects in
    ;;      other children.
    (struct.set $struct 0
      (ref.cast (ref null $struct)
        (local.get $ref)
      )
      (i32.const 1)
    )
    ;; Even in TNH mode, a child with an effect of control flow transfer
    ;; prevents us from optimizing - if the parent is not necessarily reached,
    ;; we cannot infer the child won't trap.
    (struct.set $struct 0
      (ref.cast (ref null $struct)
        (local.get $ref)
      )
      (block (result i32)
        ;; This block has type i32, to check that we don't just look for
        ;; unreachable. We must scan for any transfer of control flow in the
        ;; child of the struct.set.
        (return)
        (i32.const 1)
      )
    )
  )

  ;; TNH:      (func $cast-if-null (type $5) (param $x (ref none)) (result (ref $struct))
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (i32.const 1)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (unreachable)
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $cast-if-null (type $5) (param $x (ref none)) (result (ref $struct))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (if (result (ref none))
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:    (then
  ;; NO_TNH-NEXT:     (unreachable)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (else
  ;; NO_TNH-NEXT:     (local.get $x)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $cast-if-null (param $x (ref none)) (result (ref $struct))
    ;; We can remove the unreachable arm of the if here in TNH mode. While doing
    ;; so we must refinalize properly or else we'll hit an error in pass-debug
    ;; mode.
    (ref.cast (ref $struct)
      (if (result (ref none))
        (i32.const 1)
        (then
          (unreachable)
        )
        (else
          (local.get $x)
        )
      )
    )
  )

  ;; TNH:      (func $cast-if-null-flip (type $5) (param $x (ref none)) (result (ref $struct))
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (i32.const 1)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (unreachable)
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $cast-if-null-flip (type $5) (param $x (ref none)) (result (ref $struct))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (if (result (ref none))
  ;; NO_TNH-NEXT:    (i32.const 1)
  ;; NO_TNH-NEXT:    (then
  ;; NO_TNH-NEXT:     (local.get $x)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (else
  ;; NO_TNH-NEXT:     (unreachable)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $cast-if-null-flip (param $x (ref none)) (result (ref $struct))
    ;; As above but with arms flipped.
    (ref.cast (ref $struct)
      (if (result (ref none))
        (i32.const 1)
        (then
          (local.get $x)
        )
        (else
          (unreachable)
        )
      )
    )
  )

  ;; TNH:      (func $cast-to-bottom (type $11) (param $ref (ref any)) (param $nullable-ref anyref)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block (result (ref none))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (local.get $ref)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block (result (ref none))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (local.get $nullable-ref)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block (result (ref none))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (local.get $ref)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block (result nullref)
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (local.get $nullable-ref)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (ref.null none)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $cast-to-bottom (type $11) (param $ref (ref any)) (param $nullable-ref anyref)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result (ref none))
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (local.get $ref)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result (ref none))
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (local.get $nullable-ref)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result (ref none))
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (local.get $ref)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.cast nullref
  ;; NO_TNH-NEXT:    (local.get $nullable-ref)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $cast-to-bottom (param $ref (ref any)) (param $nullable-ref anyref)
    ;; Non-nullable casts to none must trap (regardless of whether the input is
    ;; nullable or not, the output is an impossible type).
    (drop
      (ref.cast (ref none)
        (local.get $ref)
      )
    )
    (drop
      (ref.cast (ref none)
        (local.get $nullable-ref)
      )
    )
    ;; Nullable casts to null have more possibilities. First, if the input is
    ;; non-nullable then we trap.
    (drop
      (ref.cast nullref
        (local.get $ref)
      )
    )
    ;; Second, if the value may be a null, then we either return a null or we
    ;; trap. In TNH mode we dismiss the possibility of a trap and so we can just
    ;; return a null here. (In non-TNH mode we could do a check for null etc.,
    ;; but we'd be increasing code size.)
    (drop
      (ref.cast nullref
        (local.get $nullable-ref)
      )
    )
  )

  ;; TNH:      (func $null.cast-other.effects (type $12) (param $x (ref null $struct))
  ;; TNH-NEXT:  (local $i i32)
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (local.get $x)
  ;; TNH-NEXT:   (call $import)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (local.get $x)
  ;; TNH-NEXT:   (i32.const 10)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (struct.set $struct 0
  ;; TNH-NEXT:   (local.get $x)
  ;; TNH-NEXT:   (local.tee $i
  ;; TNH-NEXT:    (i32.const 10)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $null.cast-other.effects (type $12) (param $x (ref null $struct))
  ;; NO_TNH-NEXT:  (local $i i32)
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (call $import)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (local.get $x)
  ;; NO_TNH-NEXT:   (i32.const 10)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (struct.set $struct 0
  ;; NO_TNH-NEXT:   (local.get $x)
  ;; NO_TNH-NEXT:   (local.tee $i
  ;; NO_TNH-NEXT:    (i32.const 10)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $null.cast-other.effects (param $x (ref null $struct))
    (local $i i32)
    (struct.set $struct 0
      ;; We cannot remove this ref.as_non_null, even though the struct.set will
      ;; trap if the ref is null, because that would move the trap from before
      ;; the call to the import to be after it. But in TNH we can assume it does
      ;; not trap, and remove it.
      (ref.as_non_null
        (local.get $x)
      )
      (call $import)
    )
    (struct.set $struct 0
      ;; This one can be removed even without TNH, as there are no effects after
      ;; it.
      (ref.as_non_null
        (local.get $x)
      )
      (i32.const 10)
    )
    (struct.set $struct 0
      ;; This one can be removed even without TNH, even though there are effects
      ;; in it. A tee only has local effects, which do not interfere with a
      ;; trap.
      (ref.as_non_null
        (local.get $x)
      )
      (local.tee $i
        (i32.const 10)
      )
    )
  )

  ;; TNH:      (func $select.unreachable.child (type $6) (param $x (ref $struct)) (result (ref $struct))
  ;; TNH-NEXT:  (block ;; (replaces unreachable RefCast we can't emit)
  ;; TNH-NEXT:   (drop
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:   (unreachable)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $select.unreachable.child (type $6) (param $x (ref $struct)) (result (ref $struct))
  ;; NO_TNH-NEXT:  (block ;; (replaces unreachable RefCast we can't emit)
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $select.unreachable.child (param $x (ref $struct)) (result (ref $struct))
    ;; We will turn the false arm of the select into an unreachable first, and
    ;; then process the select. While doing so we must not error, as the select
    ;; itself will still have a reachable type (a full refinalize only
    ;; happens at the very end of the function).
    (ref.cast (ref $struct)
      (select (result (ref $struct))
        (ref.as_non_null
          (ref.null none)
        )
        (local.get $x)
        (i32.const 1)
      )
    )
  )

  ;; TNH:      (func $select.unreachable.child.flip (type $6) (param $x (ref $struct)) (result (ref $struct))
  ;; TNH-NEXT:  (select
  ;; TNH-NEXT:   (local.get $x)
  ;; TNH-NEXT:   (unreachable)
  ;; TNH-NEXT:   (i32.const 1)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $select.unreachable.child.flip (type $6) (param $x (ref $struct)) (result (ref $struct))
  ;; NO_TNH-NEXT:  (select
  ;; NO_TNH-NEXT:   (local.get $x)
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:   (i32.const 1)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $select.unreachable.child.flip (param $x (ref $struct)) (result (ref $struct))
    ;; Flip case of the above.
    (ref.cast (ref $struct)
      (select (result (ref $struct))
        (local.get $x)
        (ref.as_non_null
          (ref.null none)
        )
        (i32.const 1)
      )
    )
  )

  ;; TNH:      (func $if.null.child.but.no.flow (type $void)
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (block (result (ref nofunc))
  ;; TNH-NEXT:    (drop
  ;; TNH-NEXT:     (if (result (ref nofunc))
  ;; TNH-NEXT:      (i32.const 1)
  ;; TNH-NEXT:      (then
  ;; TNH-NEXT:       (return)
  ;; TNH-NEXT:      )
  ;; TNH-NEXT:      (else
  ;; TNH-NEXT:       (unreachable)
  ;; TNH-NEXT:      )
  ;; TNH-NEXT:     )
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:    (unreachable)
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $if.null.child.but.no.flow (type $void)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result (ref nofunc))
  ;; NO_TNH-NEXT:    (drop
  ;; NO_TNH-NEXT:     (if (result (ref nofunc))
  ;; NO_TNH-NEXT:      (i32.const 1)
  ;; NO_TNH-NEXT:      (then
  ;; NO_TNH-NEXT:       (return)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (else
  ;; NO_TNH-NEXT:       (unreachable)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:    (unreachable)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $if.null.child.but.no.flow
    ;; The if's true arm has a bottom type, which the cast would trap on. But we
    ;; cannot optimize using that fact, as the null does not actually flow out -
    ;; we return from the function before. So we should not replace the if with
    ;; the false arm (that would trap, and change the behavior; tnh can remove
    ;; traps, not add them).
    (drop
      (ref.cast (ref func)
        (if (result (ref nofunc))
          (i32.const 1)
          (then
            (block (result (ref nofunc))
              (return)
            )
          )
          (else
            (unreachable)
          )
        )
      )
    )
  )

  ;; TNH:      (func $set-of-as-non-null (type $void)
  ;; TNH-NEXT:  (local $x anyref)
  ;; TNH-NEXT:  (local.set $x
  ;; TNH-NEXT:   (local.get $x)
  ;; TNH-NEXT:  )
  ;; TNH-NEXT:  (drop
  ;; TNH-NEXT:   (ref.as_non_null
  ;; TNH-NEXT:    (local.tee $x
  ;; TNH-NEXT:     (local.get $x)
  ;; TNH-NEXT:    )
  ;; TNH-NEXT:   )
  ;; TNH-NEXT:  )
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $set-of-as-non-null (type $void)
  ;; NO_TNH-NEXT:  (local $x anyref)
  ;; NO_TNH-NEXT:  (local.set $x
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (local.get $x)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (local.tee $x
  ;; NO_TNH-NEXT:     (local.get $x)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $set-of-as-non-null
    (local $x anyref)
    ;; We can remove the ref.as_non_null here because the local is nullable and
    ;; we are ignoring traps.
    ;; TODO: Should we keep the cast to let us refine the local later?
    (local.set $x
      (ref.as_non_null
        (local.get $x)
      )
    )
    ;; The same for a tee.
    (drop
      (local.tee $x
        (ref.as_non_null
          (local.get $x)
        )
      )
    )
  )


  ;; Helper functions.

  ;; TNH:      (func $get-i32 (type $2) (result i32)
  ;; TNH-NEXT:  (unreachable)
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $get-i32 (type $2) (result i32)
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $get-i32 (result i32)
    (unreachable)
  )
  ;; TNH:      (func $get-ref (type $13) (result (ref $struct))
  ;; TNH-NEXT:  (unreachable)
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $get-ref (type $13) (result (ref $struct))
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $get-ref (result (ref $struct))
    (unreachable)
  )
  ;; TNH:      (func $get-null (type $14) (result nullref)
  ;; TNH-NEXT:  (unreachable)
  ;; TNH-NEXT: )
  ;; NO_TNH:      (func $get-null (type $14) (result nullref)
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $get-null (result (ref null none))
    (unreachable)
  )
)