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

;; (remove-unused-names allows the pass to see that blocks flow values)
;; RUN: foreach %s %t wasm-opt -all --remove-unused-names --heap2local -S -o - | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $described (descriptor $descriptor) (struct (field i32)))
    (type $described (descriptor $descriptor) (struct (field i32)))
    ;; CHECK:       (type $descriptor (describes $described) (struct (field i64)))
    (type $descriptor (describes $described) (struct (field i64)))

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

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

    ;; CHECK:       (type $no-desc (struct))
    (type $no-desc (struct))

    ;; CHECK:       (type $chain-described (descriptor $chain-middle) (struct))
    (type $chain-described (descriptor $chain-middle) (struct))
    ;; CHECK:       (type $chain-middle (describes $chain-described) (descriptor $chain-descriptor) (struct))
    (type $chain-middle (describes $chain-described) (descriptor $chain-descriptor) (struct))
    ;; CHECK:       (type $chain-descriptor (describes $chain-middle) (struct))
    (type $chain-descriptor (describes $chain-middle) (struct))
  )

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

  ;; CHECK:      (type $11 (func (param (ref null (exact $super.desc)))))

  ;; CHECK:      (type $12 (func (param (ref null (exact $super)))))

  ;; CHECK:      (type $13 (func (result (ref null $super.desc))))

  ;; CHECK:      (type $14 (func (param (ref null (exact $chain-descriptor)))))

  ;; CHECK:      (type $15 (func (result (ref (exact $super)))))

  ;; CHECK:      (type $16 (func (result (ref (exact $super.desc)))))

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

  ;; CHECK:      (global $desc (ref null (exact $descriptor)) (ref.null none))
  (global $desc (ref null (exact $descriptor)) (ref.null none))

  ;; CHECK:      (func $dropped (type $10)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (local $1 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (local $2 i32)
  ;; CHECK-NEXT:  (local $3 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $3
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (global.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (local.get $2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $dropped
    (drop
      (struct.new_desc $described
        (i32.const 1)
        (global.get $desc)
      )
    )
  )

  ;; CHECK:      (func $dropped-default (type $10)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (local $1 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (global.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $dropped-default
    (drop
      (struct.new_default_desc $described
        (global.get $desc)
      )
    )
  )

  ;; CHECK:      (func $dropped-alloc-desc (type $10)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (local $1 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (local $2 i32)
  ;; CHECK-NEXT:  (local $3 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $3
  ;; CHECK-NEXT:     (struct.new $descriptor
  ;; CHECK-NEXT:      (i64.const 2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (local.get $2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $dropped-alloc-desc
    (drop
      (struct.new_desc $described
        (i32.const 1)
        (struct.new $descriptor
          (i64.const 2)
        )
      )
    )
  )

  ;; CHECK:      (func $dropped-default-alloc-desc (type $10)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (local $1 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $descriptor)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (struct.new $descriptor
  ;; CHECK-NEXT:      (i64.const 2)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $dropped-default-alloc-desc
    (drop
      (struct.new_default_desc $described
        (struct.new $descriptor
          (i64.const 2)
        )
      )
    )
  )

  ;; CHECK:      (func $get-desc (type $13) (result (ref null $super.desc))
  ;; CHECK-NEXT:  (local $0 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (struct.new_default $super.desc)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $0)
  ;; CHECK-NEXT: )
  (func $get-desc (result (ref null $super.desc))
    (ref.get_desc $super
      (struct.new_desc $super
        (struct.new $super.desc)
      )
    )
  )

  ;; CHECK:      (func $get-desc-refinalize (type $13) (result (ref null $super.desc))
  ;; CHECK-NEXT:  (local $0 (ref (exact $sub.desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $sub.desc)))
  ;; CHECK-NEXT:  (block (result (ref (exact $sub.desc)))
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (block (result nullref)
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (local.set $1
  ;; CHECK-NEXT:       (struct.new_default $sub.desc)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $0
  ;; CHECK-NEXT:       (local.get $1)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (local.get $0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $get-desc-refinalize (result (ref null $super.desc))
    ;; This block should be refinalized.
    (block (result (ref null $super.desc))
      (ref.get_desc $super
        (block (result (ref null $super))
          (struct.new_desc $sub
            (struct.new $sub.desc)
          )
        )
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-success (type $10)
  ;; CHECK-NEXT:  (local $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (local.set $desc
  ;; CHECK-NEXT:   (struct.new_default $super.desc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (local.set $2
  ;; CHECK-NEXT:       (ref.as_non_null
  ;; CHECK-NEXT:        (local.get $desc)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $1
  ;; CHECK-NEXT:       (local.get $2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (if (result nullref)
  ;; CHECK-NEXT:     (ref.eq
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:      (local.get $1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (else
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-success
    (local $desc (ref null (exact $super.desc)))
    (local.set $desc
      (struct.new $super.desc)
    )
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (struct.new_desc $super
          (local.get $desc)
        )
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-fail (type $11) (param $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (local.set $2
  ;; CHECK-NEXT:       (struct.new_default $super.desc)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $1
  ;; CHECK-NEXT:       (local.get $2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (if (result nullref)
  ;; CHECK-NEXT:     (ref.eq
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:      (local.get $1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (else
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-fail (param $desc (ref null (exact $super.desc)))
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (struct.new_desc $super
          (struct.new $super.desc)
        )
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-fail-reverse (type $11) (param $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (local.set $2
  ;; CHECK-NEXT:       (ref.as_non_null
  ;; CHECK-NEXT:        (local.get $desc)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $1
  ;; CHECK-NEXT:       (local.get $2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (if (result nullref)
  ;; CHECK-NEXT:     (ref.eq
  ;; CHECK-NEXT:      (block (result nullref)
  ;; CHECK-NEXT:       (ref.null none)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.get $1)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (else
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-fail-reverse (param $desc (ref null (exact $super.desc)))
    ;; Same as above, but change where the parameter is used.
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (struct.new_desc $super
          (local.get $desc)
        )
        (struct.new $super.desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-fail-param (type $12) (param $ref (ref null (exact $super)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-fail-param (param $ref (ref null (exact $super)))
    ;; Now cast the parameter. We know it can't have the locally allocated
    ;; descriptor, so the cast fails.
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (local.get $ref)
        (struct.new $super.desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-fail-param-effect (type $12) (param $ref (ref null (exact $super)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result (ref null (exact $super)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (block (result nullref)
  ;; CHECK-NEXT:       (ref.null none)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-fail-param-effect (param $ref (ref null (exact $super)))
    ;; Same, but with effects we cannot drop.
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (block (result (ref null (exact $super)))
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref (exact $super.desc)))
          (call $effect)
          (struct.new $super.desc)
        )
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-fail-param-nullable (type $12) (param $ref (ref null (exact $super)))
  ;; CHECK-NEXT:  (local $1 (ref null (exact $super)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $ref)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.cast nullref
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-fail-param-nullable (param $ref (ref null (exact $super)))
    ;; Now the cast admits nulls.
    (drop
      (ref.cast_desc_eq (ref null (exact $super))
        (local.get $ref)
        (struct.new $super.desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-fail-param-nullable-effect (type $12) (param $ref (ref null (exact $super)))
  ;; CHECK-NEXT:  (local $1 (ref null (exact $super)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (block (result (ref null (exact $super)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $ref)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (block (result nullref)
  ;; CHECK-NEXT:       (ref.null none)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.cast nullref
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-fail-param-nullable-effect (param $ref (ref null (exact $super)))
    ;; Now the cast admits nulls and there are effects we cannot remove.
    (drop
      (ref.cast_desc_eq (ref null (exact $super))
        (block (result (ref null (exact $super)))
          (call $effect)
          (local.get $ref)
        )
        (block (result (ref (exact $super.desc)))
          (call $effect)
          (struct.new $super.desc)
        )
      )
    )
  )

  ;; CHECK:      (func $cast-no-desc (type $11) (param $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $desc)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-no-desc (param $desc (ref null (exact $super.desc)))
    ;; The allocation does not have a descriptor, so we know the cast must fail.
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (struct.new $no-desc)
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $cast-no-desc-effect (type $11) (param $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (block (result nullref)
  ;; CHECK-NEXT:       (ref.null none)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result (ref null (exact $super.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-no-desc-effect (param $desc (ref null (exact $super.desc)))
    ;; Same, but with effects we cannot drop.
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (block (result (ref (exact $no-desc)))
          (call $effect)
          (struct.new $no-desc)
        )
        (block (result (ref null (exact $super.desc)))
          (call $effect)
          (local.get $desc)
        )
      )
    )
  )

  ;; CHECK:      (func $cast-no-desc-nullable (type $11) (param $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (local.get $desc)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-no-desc-nullable (param $desc (ref null (exact $super.desc)))
    ;; The allocation does not have a descriptor, so we know the cast must fail.
    ;; Although the cast admits nulls, we know we don't have a null here, so we
    ;; don't need to preserve a null cast.
    (drop
      (ref.cast_desc_eq (ref null (exact $super))
        (struct.new $no-desc)
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $cast-no-desc-nullable-effect (type $11) (param $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (block (result nullref)
  ;; CHECK-NEXT:       (ref.null none)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result (ref null (exact $super.desc)))
  ;; CHECK-NEXT:      (call $effect)
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-no-desc-nullable-effect (param $desc (ref null (exact $super.desc)))
    ;; Same, but with effects we cannot drop.
    (drop
      (ref.cast_desc_eq (ref null (exact $super))
        (block (result (ref (exact $no-desc)))
          (call $effect)
          (struct.new $no-desc)
        )
        (block (result (ref null (exact $super.desc)))
          (call $effect)
          (local.get $desc)
        )
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-and-ref (type $14) (param $desc (ref null (exact $chain-descriptor)))
  ;; CHECK-NEXT:  (local $middle (ref null (exact $chain-middle)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $chain-descriptor)))
  ;; CHECK-NEXT:  (local $3 (ref (exact $chain-descriptor)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $3
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (local.get $3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-and-ref (param $desc (ref null (exact $chain-descriptor)))
    ;; The same allocation flows into both the descriptor and the reference. The
    ;; cast must fail because a value cannot be its own descriptor. We make sure
    ;; the descriptor itself has a descriptor so it is not handled by the same
    ;; logic as the previous test.
    (local $middle (ref null (exact $chain-middle)))
    (local.set $middle
      (struct.new_desc $chain-middle
        (local.get $desc)
      )
    )
    (drop
      (ref.cast_desc_eq (ref (exact $chain-described))
        (local.get $middle)
        (local.get $middle)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-and-ref-nullable (type $14) (param $desc (ref null (exact $chain-descriptor)))
  ;; CHECK-NEXT:  (local $middle (ref null (exact $chain-middle)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $chain-descriptor)))
  ;; CHECK-NEXT:  (local $3 (ref (exact $chain-descriptor)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $3
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (local.get $3)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-and-ref-nullable (param $desc (ref null (exact $chain-descriptor)))
    ;; Same, but now the cast allows nulls. It should still trap.
    (local $middle (ref null (exact $chain-middle)))
    (local.set $middle
      (struct.new_desc $chain-middle
        (local.get $desc)
      )
    )
    (drop
      (ref.cast_desc_eq (ref null (exact $chain-described))
        (local.get $middle)
        (local.get $middle)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-and-ref-tee (type $10)
  ;; CHECK-NEXT:  (local $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-and-ref-tee
    ;; The same allocation flows into both the descriptor and the reference
    ;; again, but now it uses a tee. The allocation does not have a descriptor.
    (local $desc (ref null (exact $super.desc)))
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        (local.tee $desc
          (struct.new $super.desc)
        )
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-and-ref-tee-nullable (type $10)
  ;; CHECK-NEXT:  (local $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-and-ref-tee-nullable
    ;; Same, but the cast allows nulls. It should still trap.
    (local $desc (ref null (exact $super.desc)))
    (drop
      (ref.cast_desc_eq (ref null (exact $super))
        (local.tee $desc
          (struct.new $super.desc)
        )
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-stale-parent (type $15) (result (ref (exact $super)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result (ref null $super.desc))
  ;; CHECK-NEXT:    (call $effect)
  ;; CHECK-NEXT:    (block (result nullref)
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-stale-parent (result (ref (exact $super)))
    (ref.cast_desc_eq (ref (exact $super))
      ;; We will optimize this allocation first, causing the parent
      ;; ref.cast_desc_eq to be optimized out. The parent map will no longer be up
      ;; to date when we optimize the second allocation, but we should sill be
      ;; able to optimize successfully without crashing.
      (struct.new_default $no-desc)
      (block (result (ref (exact $super.desc)))
        (call $effect)
        (struct.new_default $super.desc)
      )
    )
  )

  ;; CHECK:      (func $cast-desc-eq-stale-parent-escape (type $16) (result (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (local $desc (ref null (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (ref.as_non_null
  ;; CHECK-NEXT:   (local.get $desc)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $cast-desc-eq-stale-parent-escape (result (ref (exact $super.desc)))
    (local $desc (ref (exact $super.desc)))
    (drop
      (ref.cast_desc_eq (ref (exact $super))
        ;; Same as above, but now the second alloocation escapes. We should still
        ;; optimize the first allocation and the cast, and we should still not
        ;; crash.
        (struct.new_default $no-desc)
        (local.tee $desc
          (struct.new_default $super.desc)
        )
      )
    )
    (local.get $desc)
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (sub (descriptor $desc) (struct)))
    (type $struct (sub (descriptor $desc) (struct)))
    ;; CHECK:       (type $desc (describes $struct) (struct))
    (type $desc (describes $struct) (struct))
  )

  ;; CHECK:      (type $2 (func (result (ref (exact $desc)))))

  ;; CHECK:      (type $3 (func (param (ref null (exact $desc))) (result (ref (exact $desc)))))

  ;; CHECK:      (func $null (type $2) (result (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $0 (ref none))
  ;; CHECK-NEXT:  (local $1 (ref none))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $0)
  ;; CHECK-NEXT: )
  (func $null (result (ref (exact $desc)))
    ;; Read a null descriptor from a struct.new we can convert to locals. We do
    ;; not end up with a (ref (exact $desc)) here, since this will trap, so we
    ;; emit an unreachable.
    (ref.get_desc $struct
      (struct.new_default_desc $struct
        (ref.null none)
      )
    )
  )

  ;; CHECK:      (func $nullable-param (type $3) (param $desc (ref null (exact $desc))) (result (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $1)
  ;; CHECK-NEXT: )
  (func $nullable-param (param $desc (ref null (exact $desc))) (result (ref (exact $desc)))
    ;; Read a null descriptor from a nullable param.
    (ref.get_desc $struct
      (struct.new_default_desc $struct
        (local.get $desc)
      )
    )
  )

  ;; CHECK:      (func $nullable-local (type $2) (result (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $desc (ref null (exact $desc)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $desc)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (local.get $2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.get $1)
  ;; CHECK-NEXT: )
  (func $nullable-local (result (ref (exact $desc)))
    (local $desc (ref null (exact $desc)))
    ;; Read a null descriptor from a nullable local.
    (ref.get_desc $struct
      (struct.new_default_desc $struct
        (local.get $desc)
      )
    )
  )
)

;; A definitely-failing descriptor cast. We have two pairs of descriptor/
;; describee, and create an $A2 that we try to cast to the unrelated $A.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (descriptor $B) (struct))
    (type $A (descriptor $B) (struct))
    ;; CHECK:       (type $B (describes $A) (struct))
    (type $B (describes $A) (struct))

    ;; CHECK:       (type $A2 (descriptor $B2) (struct))
    (type $A2 (descriptor $B2) (struct))
    ;; CHECK:       (type $B2 (describes $A2) (struct))
    (type $B2 (describes $A2) (struct))
  )

  ;; CHECK:      (type $4 (func (result (ref $A))))

  ;; CHECK:      (func $A (type $4) (result (ref $A))
  ;; CHECK-NEXT:  (local $0 (ref (exact $B2)))
  ;; CHECK-NEXT:  (local $1 (ref (exact $B2)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $1
  ;; CHECK-NEXT:     (struct.new_default $B2)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (local.set $0
  ;; CHECK-NEXT:     (local.get $1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $A (result (ref $A))
    (ref.cast_desc_eq (ref $A)
      (struct.new_default_desc $A2
        (struct.new_default $B2)
      )
      (struct.new_default $B)
    )
  )
)

(module
  (rec
    (type $A (descriptor $B) (struct))
    (type $B (sub (describes $A) (struct)))
  )
  ;; CHECK:      (type $0 (func))

  ;; CHECK:      (func $test (type $0)
  ;; CHECK-NEXT:  (local $0 (ref none))
  ;; CHECK-NEXT:  (local $1 (ref none))
  ;; CHECK-NEXT:  (block $block
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (br_on_null $block
  ;; CHECK-NEXT:     (block (result (ref none))
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (block (result nullref)
  ;; CHECK-NEXT:        (local.set $1
  ;; CHECK-NEXT:         (ref.as_non_null
  ;; CHECK-NEXT:          (ref.null none)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (local.set $0
  ;; CHECK-NEXT:         (local.get $1)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (ref.null none)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.get $0)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    ;; After removing the ref.get_desc, the null descriptor falls through, and
    ;; we must update the br_on_null's type, or internal validation errors.
    (block $block
      (drop
        (br_on_null $block
          (ref.get_desc $A
            (struct.new_default_desc $A
              (ref.null none)
            )
          )
        )
      )
      (unreachable)
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (descriptor $B) (struct (field v128)))
    (type $A (descriptor $B) (struct (field v128)))
    ;; CHECK:       (type $B (describes $A) (struct))
    (type $B (describes $A) (struct))
  )

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

  ;; CHECK:      (func $test (type $2)
  ;; CHECK-NEXT:  (local $B (ref null $B))
  ;; CHECK-NEXT:  (local $v v128)
  ;; CHECK-NEXT:  (local $2 v128)
  ;; CHECK-NEXT:  (local $3 (ref none))
  ;; CHECK-NEXT:  (local $4 (ref none))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.tee $v
  ;; CHECK-NEXT:   (block ;; (replaces unreachable StructGet we can't emit)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (block (result nullref)
  ;; CHECK-NEXT:        (local.set $4
  ;; CHECK-NEXT:         (ref.as_non_null
  ;; CHECK-NEXT:          (ref.null none)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (local.set $2
  ;; CHECK-NEXT:         (v128.const i32x4 0x00000000 0x00000000 0x00000000 0x00000000)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (local.set $3
  ;; CHECK-NEXT:         (local.get $4)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (ref.null none)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (ref.null none)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    (local $B (ref null $B))
    (local $v v128)

    ;; We can optimize a few times here. As we do so, the local.set becomes
    ;; unreachable, as its descriptor is null. Later work will replace the
    ;; nested struct.get there, which was unreachable, with a local.get of a
    ;; v128, a concrete type, causing an error as now the local.set is
    ;; unreachable but the child is not. To avoid this problem, we should not
    ;; modify unreachable code.

    (drop
      (ref.as_non_null
        (local.tee $B
          (struct.new_default $B)
        )
      )
    )
    (local.set $v
      (struct.get $A 0
        (ref.cast_desc_eq (ref $A)
          (struct.new_default_desc $A
            (ref.as_non_null
              (ref.null none)
            )
          )
          (ref.as_non_null
            (local.get $B)
          )
        )
      )
    )
  )
)

;; A chain of descriptors, where initial optimizations influence later ones.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (shared (descriptor $B) (struct)))
    (type $A (shared (descriptor $B) (struct)))
    ;; CHECK:       (type $B (sub (shared (describes $A) (descriptor $C) (struct))))
    (type $B (sub (shared (describes $A) (descriptor $C) (struct))))
    ;; CHECK:       (type $C (sub (shared (describes $B) (struct))))
    (type $C (sub (shared (describes $B) (struct))))
  )

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

  ;; CHECK:      (func $test (type $3) (result (ref (shared any)))
  ;; CHECK-NEXT:  (local $temp (ref $C))
  ;; CHECK-NEXT:  (local $1 (ref (shared none)))
  ;; CHECK-NEXT:  (local $2 (ref (shared none)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result (ref null (shared none)))
  ;; CHECK-NEXT:    (ref.null (shared none))
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (block
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (block
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (block (result (ref null (shared none)))
  ;; CHECK-NEXT:       (local.set $2
  ;; CHECK-NEXT:        (ref.as_non_null
  ;; CHECK-NEXT:         (ref.null (shared none))
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (local.set $1
  ;; CHECK-NEXT:        (local.get $2)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (ref.null (shared none))
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (drop
  ;; CHECK-NEXT:      (ref.null (shared none))
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (unreachable)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (block (result (ref null (shared none)))
  ;; CHECK-NEXT:     (ref.null (shared none))
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (unreachable)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (result (ref (shared any)))
    (local $temp (ref $C))
    (local.set $temp
      ;; We optimize this first, making the |local.get| below unreachable, and
      ;; making that inner ref.cast_desc_eq unreachable, which leads to the
      ;; |struct.new_default $B| being dropped, and in particular having a new
      ;; parent (the drop). We should not get confused and error internally.
      (struct.new_default $C)
    )
    (ref.cast_desc_eq (ref $B)
      (ref.cast_desc_eq (ref $B)
        (struct.new_default_desc $B
          (ref.null (shared none))
        )
        (local.get $temp)
      )
      (struct.new_default $C)
    )
  )
)

(module
  (rec
   ;; CHECK:      (rec
   ;; CHECK-NEXT:  (type $struct (sub (descriptor $desc) (struct)))
   (type $struct (sub (descriptor $desc) (struct)))
   ;; CHECK:       (type $desc (describes $struct) (struct))
   (type $desc (sub final (describes $struct) (struct)))
  )

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

  ;; CHECK:      (func $test (type $2) (result i32)
  ;; CHECK-NEXT:  (local $desc (ref $desc))
  ;; CHECK-NEXT:  (local $1 (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $2 (ref (exact $desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (ref.is_null
  ;; CHECK-NEXT:   (block
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (local.set $2
  ;; CHECK-NEXT:       (struct.new_default $desc)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $1
  ;; CHECK-NEXT:       (local.get $2)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.null none)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (result i32)
    (local $desc (ref $desc))
    (local.set $desc
      (struct.new_default $desc)
    )
    ;; After we optimize the struct.new above, the local.get below will get
    ;; removed, and we will see that the ref.cast_desc_eq traps (the input
    ;; descriptor differs from the one we test, the allocation we just
    ;; removed). When optimizing that, we should not emit invalid IR for the
    ;; ref.is_null: It has an i32 result normally, but in unreachable code it
    ;; must remain unreachable.
    (ref.is_null
      (ref.cast_desc_eq (ref $struct)
        (struct.new_default_desc $struct
          (struct.new_default $desc)
        )
        (local.get $desc)
      )
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (descriptor $desc) (struct))
    (type $struct (descriptor $desc) (struct))
    ;; CHECK:       (type $desc (sub (describes $struct) (struct (field funcref))))
    (type $desc (sub (describes $struct) (struct (field funcref))))
  )

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

  ;; CHECK:      (func $test (type $2)
  ;; CHECK-NEXT:  (local $desc (ref $desc))
  ;; CHECK-NEXT:  (local $func funcref)
  ;; CHECK-NEXT:  (local $2 funcref)
  ;; CHECK-NEXT:  (local $3 (ref (exact $desc)))
  ;; CHECK-NEXT:  (local $4 (ref (exact $desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result nullref)
  ;; CHECK-NEXT:    (local.set $2
  ;; CHECK-NEXT:     (ref.null nofunc)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (ref.null none)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (local.tee $func
  ;; CHECK-NEXT:   (block ;; (replaces unreachable StructGet we can't emit)
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (block ;; (replaces unreachable RefGetDesc we can't emit)
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (block
  ;; CHECK-NEXT:        (drop
  ;; CHECK-NEXT:         (block (result nullref)
  ;; CHECK-NEXT:          (local.set $4
  ;; CHECK-NEXT:           (struct.new_default $desc)
  ;; CHECK-NEXT:          )
  ;; CHECK-NEXT:          (local.set $3
  ;; CHECK-NEXT:           (local.get $4)
  ;; CHECK-NEXT:          )
  ;; CHECK-NEXT:          (ref.null none)
  ;; CHECK-NEXT:         )
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (drop
  ;; CHECK-NEXT:         (ref.null none)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:        (unreachable)
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (unreachable)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    (local $desc (ref $desc))
    (local $func funcref)

    (local.set $desc
      (struct.new_default $desc)
    )

    (local.set $func
      (struct.get $desc 0
        ;; This ref.get_desc will become unreachable (as the desc does not
        ;; match, after expanding locals). We should not generate invalid code
        ;; from that point - it is unreachable, and we can leave it as is.
        (ref.get_desc $struct
          (ref.cast_desc_eq (ref $struct)
            (struct.new_default_desc $struct
              (struct.new_default $desc)
            )
            (local.get $desc)
          )
        )
      )
    )
  )
)
