;; NOTE: Assertions have been generated by update_lit_checks.py and should not be edited.

;; RUN: wasm-opt %s -all --closed-world -tnh --abstract-type-refining -S -o - | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $uninstantiated (sub (descriptor $uninstantiated.desc) (struct)))
    (type $uninstantiated (sub (descriptor $uninstantiated.desc) (struct)))
    ;; CHECK:       (type $instantiated (sub $uninstantiated (descriptor $instantiated.desc) (struct)))

    ;; CHECK:       (type $uninstantiated.desc (sub (describes $uninstantiated) (struct)))
    (type $uninstantiated.desc (sub (describes $uninstantiated) (struct)))
    (type $instantiated (sub $uninstantiated (descriptor $instantiated.desc) (struct)))
    ;; CHECK:       (type $instantiated.desc (sub $uninstantiated.desc (describes $instantiated) (struct)))
    (type $instantiated.desc (sub $uninstantiated.desc (describes $instantiated) (struct)))
  )

  ;; CHECK:      (import "" "" (func $effect (type $5)))
  (import "" "" (func $effect))

  ;; CHECK:      (global $nullable-desc (ref null (exact $uninstantiated.desc)) (ref.null none))
  (global $nullable-desc (ref null (exact $uninstantiated.desc)) (ref.null none))

  ;; CHECK:      (global $instantiated (ref $instantiated) (struct.new_default_desc $instantiated
  ;; CHECK-NEXT:  (struct.new_default $instantiated.desc)
  ;; CHECK-NEXT: ))
  (global $instantiated (ref $instantiated)
    (struct.new_desc $instantiated
      (struct.new $instantiated.desc)
    )
  )

  ;; CHECK:      (func $ref-cast (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast (ref none)
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast (param $ref anyref) (result anyref)
    ;; We assume traps never happen, so this cast will never fail. That's only
    ;; possible if it is never reached, so it would be fine to update the cast
    ;; target. We can do slightly better by making the cast uninhabitable,
    ;; though.
    (ref.cast (ref (exact $uninstantiated))
      (local.get $ref)
    )
  )

  ;; CHECK:      (func $ref-cast-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast (ref none)
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (ref.cast (ref (exact $uninstantiated))
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
    )
  )

  ;; CHECK:      (func $ref-cast-null (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast nullref
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-null (param $ref anyref) (result anyref)
    ;; When the cast admits null, it might be reached with null values, so we
    ;; have to optimize it to a null check.
    (ref.cast (ref null (exact $uninstantiated))
      (local.get $ref)
    )
  )

  ;; CHECK:      (func $ref-cast-null-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast nullref
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-null-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (ref.cast (ref null (exact $uninstantiated))
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
    )
  )

  ;; CHECK:      (func $ref-cast-desc-eq (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast (ref none)
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-desc-eq (param $ref anyref) (result anyref)
    ;; Unlike the normal casts, it would not be safe to update the target of a
    ;; descriptor cast because it is determined by the descriptor operand and
    ;; changing it would be invalid. But we optimize to a bottom cast anyway, so
    ;; there is no problem.
    (ref.cast_desc_eq (ref (exact $uninstantiated))
      (local.get $ref)
      (struct.new $uninstantiated.desc)
    )
  )

  ;; CHECK:      (func $ref-cast-desc-eq-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (local.set $1
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $2
  ;; CHECK-NEXT:   (block (result (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (struct.new_default $uninstantiated.desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (ref.cast (ref none)
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-desc-eq-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (ref.cast_desc_eq (ref (exact $uninstantiated))
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
      (block (result (ref (exact $uninstantiated.desc)))
        (call $effect)
        (struct.new $uninstantiated.desc)
      )
    )
  )

  ;; CHECK:      (func $ref-cast-desc-eq-null (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast nullref
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-desc-eq-null (param $ref anyref) (result anyref)
    ;; If the descriptor cast admits null, we optimize it to a null check.
    (ref.cast_desc_eq (ref null (exact $uninstantiated))
      (local.get $ref)
      (struct.new $uninstantiated.desc)
    )
  )

  ;; CHECK:      (func $ref-cast-desc-eq-null-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (local.set $1
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $2
  ;; CHECK-NEXT:   (block (result (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (struct.new_default $uninstantiated.desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (ref.cast nullref
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-desc-eq-null-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (ref.cast_desc_eq (ref null (exact $uninstantiated))
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
      (block (result (ref (exact $uninstantiated.desc)))
        (call $effect)
        (struct.new $uninstantiated.desc)
      )
    )
  )

  ;; CHECK:      (func $ref-cast-nullable-desc (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (ref.cast (ref none)
  ;; CHECK-NEXT:   (local.get $ref)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-nullable-desc (param $ref anyref) (result anyref)
    ;; Now the descriptor is nullable, but we assume traps never happen, so
    ;; we don't need to add a null check on it.
    (ref.cast_desc_eq (ref (exact $uninstantiated))
      (local.get $ref)
      (global.get $nullable-desc)

    )
  )

  ;; CHECK:      (func $ref-cast-nullable-desc-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref null (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (local.set $1
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.set $2
  ;; CHECK-NEXT:   (block (result (ref null (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (global.get $nullable-desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (ref.cast (ref none)
  ;; CHECK-NEXT:   (local.get $1)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast-nullable-desc-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (ref.cast_desc_eq (ref (exact $uninstantiated))
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
      (block (result (ref null (exact $uninstantiated.desc)))
        (call $effect)
        (global.get $nullable-desc)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast $l anyref (ref none)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Since branching casts never trap anyway, we have to assume they will be
      ;; reached. We know this cast cannot succeed, so it would be incorrect to
      ;; optimize the cast target to (ref (exact $instantiated)). We optimize
      ;; the cast type to an uninhabitable type instead.
      (br_on_cast $l anyref (ref (exact $uninstantiated))
        (local.get $ref)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast $l anyref (ref none)
  ;; CHECK-NEXT:    (block (result anyref)
  ;; CHECK-NEXT:     (call $effect)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast $l anyref (ref (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-null (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast $l anyref nullref
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-null (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Same, but now the cast admits nulls, so we must optimize it to a null
      ;; check.
      (br_on_cast $l anyref (ref null (exact $uninstantiated))
        (local.get $ref)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-null-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast $l anyref nullref
  ;; CHECK-NEXT:    (block (result anyref)
  ;; CHECK-NEXT:     (call $effect)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-null-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast $l anyref (ref null (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (br_on_cast $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; As with normal br_on_cast, we know this cast will never succeed, so we
      ;; optimize the branch target to be uninhabitable.
      (br_on_cast_desc_eq $l anyref (ref (exact $uninstantiated))
        (local.get $ref)
        (struct.new $uninstantiated.desc)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result anyref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (block (result (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (struct.new_default $uninstantiated.desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast_desc_eq $l anyref (ref (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref (exact $uninstantiated.desc)))
          (call $effect)
          (struct.new $uninstantiated.desc)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-null (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref any))
  ;; CHECK-NEXT:    (br_on_cast $l anyref nullref
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-null (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Same, but now the cast admits nulls, so we must optimize it to a null
      ;; check.
      (br_on_cast_desc_eq $l anyref (ref null (exact $uninstantiated))
        (local.get $ref)
        (struct.new $uninstantiated.desc)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-null-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref any))
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result anyref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (block (result (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (struct.new_default $uninstantiated.desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast $l anyref nullref
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-null-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast_desc_eq $l anyref (ref null (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref (exact $uninstantiated.desc)))
          (call $effect)
          (struct.new $uninstantiated.desc)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-nullable-desc (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (br_on_cast $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-nullable-desc (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Now the descriptor is nullable, but we assume traps never happen, so
      ;; we don't need to add a null check on it.
      (br_on_cast_desc_eq $l anyref (ref (exact $uninstantiated))
        (local.get $ref)
        (global.get $nullable-desc)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-nullable-desc-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref null (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result anyref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result anyref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (block (result (ref null (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (global.get $nullable-desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-nullable-desc-effect (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Same, but with side effects we cannot drop.
      (br_on_cast_desc_eq $l anyref (ref (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref null (exact $uninstantiated.desc)))
          (call $effect)
          (global.get $nullable-desc)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-fail (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-fail (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Now we know the cast will always be taken.
      (br_on_cast_fail $l anyref (ref (exact $uninstantiated))
        (local.get $ref)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-fail-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:    (block (result anyref)
  ;; CHECK-NEXT:     (call $effect)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-fail-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast_fail $l anyref (ref (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-fail-null (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast_fail $l anyref nullref
  ;; CHECK-NEXT:    (local.get $ref)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-fail-null (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Now we know the cast will always be taken, except on nulls.
      (br_on_cast_fail $l anyref (ref null (exact $uninstantiated))
        (local.get $ref)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-fail-null-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (br_on_cast_fail $l anyref nullref
  ;; CHECK-NEXT:    (block (result anyref)
  ;; CHECK-NEXT:     (call $effect)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-fail-null-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast_fail $l anyref (ref null (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-fail (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref none))
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-fail (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Now we know the cast will always be taken.
      (br_on_cast_desc_eq_fail $l anyref (ref (exact $uninstantiated))
        (local.get $ref)
        (struct.new $uninstantiated.desc)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-fail-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref none))
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result anyref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (block (result (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (struct.new_default $uninstantiated.desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-fail-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast_desc_eq_fail $l anyref (ref (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref (exact $uninstantiated.desc)))
          (call $effect)
          (struct.new $uninstantiated.desc)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-fail-null (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref nullref
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-fail-null (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Now we know the cast will always be taken, except on nulls.
      (br_on_cast_desc_eq_fail $l anyref (ref null (exact $uninstantiated))
        (local.get $ref)
        (struct.new $uninstantiated.desc)
      )
    )
  )


  ;; CHECK:      (func $br-on-cast-desc-eq-fail-null-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result anyref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (block (result (ref (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (struct.new_default $uninstantiated.desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref nullref
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-fail-null-effect (param $ref anyref) (result anyref)
    ;; Same, but with side effects we cannot drop.
    (block $l (result anyref)
      (br_on_cast_desc_eq_fail $l anyref (ref null (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref (exact $uninstantiated.desc)))
          (call $effect)
          (struct.new $uninstantiated.desc)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-nullable-desc-fail (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref none))
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-nullable-desc-fail (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Now the descriptor is nullable, but we assume traps never happen, so
      ;; we don't need to add a null check on it.
      (br_on_cast_desc_eq_fail $l anyref (ref (exact $uninstantiated))
        (local.get $ref)
        (global.get $nullable-desc)
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-nullable-desc-fail-effect (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 anyref)
  ;; CHECK-NEXT:  (local $2 (ref null (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref none))
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result anyref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (block (result (ref null (exact $uninstantiated.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (global.get $nullable-desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-nullable-desc-fail-effect (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; Same, but with side effects we cannot drop.
      (br_on_cast_desc_eq_fail $l anyref (ref (exact $uninstantiated))
        (block (result anyref)
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref null (exact $uninstantiated.desc)))
          (call $effect)
          (global.get $nullable-desc)
        )
      )
    )
  )

  ;; CHECK:      (func $br-on-cast-desc-eq-fail-squared (type $3) (param $ref anyref) (result anyref)
  ;; CHECK-NEXT:  (local $1 (ref none))
  ;; CHECK-NEXT:  (block $l (result anyref)
  ;; CHECK-NEXT:   (block (result (ref none))
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result (ref none))
  ;; CHECK-NEXT:      (br_on_cast_fail $l anyref (ref none)
  ;; CHECK-NEXT:       (local.get $ref)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (br_on_cast_fail $l (ref none) (ref none)
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-desc-eq-fail-squared (param $ref anyref) (result anyref)
    (block $l (result anyref)
      ;; We should update the type of the inner br_on_cast_desc_eq_fail after
      ;; optimizing it so that the local produced when optimizing the outer
      ;; br_on_cast_desc_eq_fail has the more refined type.
      (br_on_cast_desc_eq_fail $l anyref (ref (exact $uninstantiated))
        (br_on_cast_desc_eq_fail $l anyref (ref (exact $uninstantiated))
          (local.get $ref)
          (struct.new $uninstantiated.desc)
        )
        (struct.new $uninstantiated.desc)
      )
    )
  )
)
