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

;; RUN: foreach %s %t wasm-opt --abstract-type-refining --remove-unused-types --traps-never-happen \
;; RUN:     -all --closed-world --preserve-type-order -S -o - | filecheck %s --check-prefix=YESTNH
;; RUN: foreach %s %t wasm-opt --abstract-type-refining --remove-unused-types \
;; RUN:     -all --closed-world --preserve-type-order -S -o - | filecheck %s --check-prefix=NO_TNH

;; Run in both TNH and non-TNH mode.

(module
  ;; We should not try to generate invalid types by removing the subtype
  ;; relation between $B.desc and $A.desc.
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $A (sub (descriptor $A.desc) (struct)))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $A (sub (descriptor $A.desc) (struct)))
    (type $A (sub (descriptor $A.desc) (struct)))
    ;; YESTNH:       (type $A.desc (sub (describes $A) (struct)))
    ;; NO_TNH:       (type $A.desc (sub (describes $A) (struct)))
    (type $A.desc (sub (describes $A) (struct)))
    ;; YESTNH:       (type $B (sub $A (descriptor $B.desc) (struct)))
    ;; NO_TNH:       (type $B (sub $A (descriptor $B.desc) (struct)))
    (type $B (sub $A (descriptor $B.desc) (struct)))
    ;; YESTNH:       (type $B.desc (sub $A.desc (describes $B) (struct)))
    ;; NO_TNH:       (type $B.desc (sub $A.desc (describes $B) (struct)))
    (type $B.desc (sub $A.desc (describes $B) (struct)))
  )
  ;; YESTNH:      (global $g (ref (exact $B)) (struct.new_default_desc $B
  ;; YESTNH-NEXT:  (ref.null none)
  ;; YESTNH-NEXT: ))
  ;; NO_TNH:      (global $g (ref (exact $B)) (struct.new_default_desc $B
  ;; NO_TNH-NEXT:  (ref.null none)
  ;; NO_TNH-NEXT: ))
  (global $g (ref (exact $B))
    (struct.new_default_desc $B
      (ref.null none)
    )
  )
)

(module
  ;; Same as above, but now we should see references to $A.desc and $B.desc
  ;; optimized to nullref because they are not instantiated. Similarly, $A is
  ;; optimized to $B with TNH.
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $A (sub (descriptor $A.desc) (struct (field (ref null $B)) (field (ref null $B)))))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $A (sub (descriptor $A.desc) (struct (field (ref null $A)) (field (ref null $B)))))
    (type $A (sub (descriptor $A.desc) (struct (field (ref null $A) (ref null $B)))))
    ;; YESTNH:       (type $A.desc (sub (describes $A) (struct (field nullref) (field nullref))))
    ;; NO_TNH:       (type $A.desc (sub (describes $A) (struct (field nullref) (field nullref))))
    (type $A.desc (sub (describes $A) (struct (field (ref null $A.desc) (ref null $B.desc)))))
    ;; YESTNH:       (type $B (sub $A (descriptor $B.desc) (struct (field (ref null $B)) (field (ref null $B)))))
    ;; NO_TNH:       (type $B (sub $A (descriptor $B.desc) (struct (field (ref null $A)) (field (ref null $B)))))
    (type $B (sub $A (descriptor $B.desc) (struct (field (ref null $A) (ref null $B)))))
    ;; YESTNH:       (type $B.desc (sub $A.desc (describes $B) (struct (field nullref) (field nullref))))
    ;; NO_TNH:       (type $B.desc (sub $A.desc (describes $B) (struct (field nullref) (field nullref))))
    (type $B.desc (sub $A.desc (describes $B) (struct (field (ref null $A.desc) (ref null $B.desc)))))
  )

  ;; YESTNH:      (global $g (ref (exact $B)) (struct.new_default_desc $B
  ;; YESTNH-NEXT:  (ref.null none)
  ;; YESTNH-NEXT: ))
  ;; NO_TNH:      (global $g (ref (exact $B)) (struct.new_default_desc $B
  ;; NO_TNH-NEXT:  (ref.null none)
  ;; NO_TNH-NEXT: ))
  (global $g (ref (exact $B))
    (struct.new_default_desc $B
      (ref.null none)
    )
  )
)

(module
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $struct (descriptor $desc) (struct))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $struct (descriptor $desc) (struct))
    (type $struct (descriptor $desc) (struct))
    ;; YESTNH:       (type $desc (describes $struct) (struct))
    ;; NO_TNH:       (type $desc (describes $struct) (struct))
    (type $desc (describes $struct) (struct))
  )

  ;; YESTNH:       (type $2 (func (param anyref (ref null $desc)) (result nullref)))

  ;; YESTNH:       (type $3 (func (param anyref (ref $desc)) (result nullref)))

  ;; YESTNH:      (type $4 (func))

  ;; YESTNH:      (import "" "" (func $effect (type $4)))
  ;; NO_TNH:       (type $2 (func (param anyref (ref null $desc)) (result nullref)))

  ;; NO_TNH:       (type $3 (func (param anyref (ref $desc)) (result nullref)))

  ;; NO_TNH:      (type $4 (func))

  ;; NO_TNH:      (import "" "" (func $effect (type $4)))
  (import "" "" (func $effect))

  ;; YESTNH:      (global $desc (ref $desc) (struct.new_default $desc))
  ;; NO_TNH:      (global $desc (ref $desc) (struct.new_default $desc))
  (global $desc (ref $desc) (struct.new $desc))

  ;; YESTNH:      (func $cast-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (ref.cast nullref
  ;; YESTNH-NEXT:   (local.get $ref)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $cast-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 (ref $desc))
  ;; NO_TNH-NEXT:  (local.set $2
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (local.get $desc)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (ref.cast nullref
  ;; NO_TNH-NEXT:   (local.get $ref)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $cast-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    ;; $struct will be optimized to nullref, but $desc will not. We must
    ;; optimize out this cast because otherwise ReFinalization would make its
    ;; result (ref $struct) again and it would not be valid for the optimized
    ;; function result type.
    (ref.cast_desc_eq (ref null $struct)
      (local.get $ref)
      (local.get $desc)
    )
  )

  ;; YESTNH:      (func $cast-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (local $2 anyref)
  ;; YESTNH-NEXT:  (local $3 (ref null $desc))
  ;; YESTNH-NEXT:  (local.set $2
  ;; YESTNH-NEXT:   (block (result anyref)
  ;; YESTNH-NEXT:    (call $effect)
  ;; YESTNH-NEXT:    (local.get $ref)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (local.set $3
  ;; YESTNH-NEXT:   (block (result (ref null $desc))
  ;; YESTNH-NEXT:    (call $effect)
  ;; YESTNH-NEXT:    (local.get $desc)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (ref.cast nullref
  ;; YESTNH-NEXT:   (local.get $2)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $cast-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 anyref)
  ;; NO_TNH-NEXT:  (local $3 (ref $desc))
  ;; NO_TNH-NEXT:  (local.set $2
  ;; NO_TNH-NEXT:   (block (result anyref)
  ;; NO_TNH-NEXT:    (call $effect)
  ;; NO_TNH-NEXT:    (local.get $ref)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (local.set $3
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (block (result (ref null $desc))
  ;; NO_TNH-NEXT:     (call $effect)
  ;; NO_TNH-NEXT:     (local.get $desc)
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (ref.cast nullref
  ;; NO_TNH-NEXT:   (local.get $2)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $cast-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    ;; Same, but with side effects we cannot drop.
    (ref.cast_desc_eq (ref null $struct)
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
      (block (result (ref null $desc))
        (call $effect)
        (local.get $desc)
      )
    )
  )

  ;; YESTNH:      (func $cast-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (ref.cast (ref none)
  ;; YESTNH-NEXT:   (local.get $ref)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $cast-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 (ref $desc))
  ;; NO_TNH-NEXT:  (local.set $2
  ;; NO_TNH-NEXT:   (ref.as_non_null
  ;; NO_TNH-NEXT:    (local.get $desc)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (ref.cast (ref none)
  ;; NO_TNH-NEXT:   (local.get $ref)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $cast-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    ;; Same, but now the cast does not admit null.
    (ref.cast_desc_eq (ref $struct)
      (local.get $ref)
      (local.get $desc)
    )
  )

  ;; YESTNH:      (func $cast-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; YESTNH-NEXT:  (ref.cast (ref none)
  ;; YESTNH-NEXT:   (local.get $ref)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $cast-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (ref.cast (ref none)
  ;; NO_TNH-NEXT:   (local.get $ref)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $cast-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct))
    ;; Same, but now the descriptor is additionally non-null.
    (ref.cast_desc_eq (ref $struct)
      (local.get $ref)
      (local.get $desc)
    )
  )

  ;; YESTNH:      (func $cast-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; YESTNH-NEXT:  (local $2 anyref)
  ;; YESTNH-NEXT:  (local $3 (ref $desc))
  ;; YESTNH-NEXT:  (local.set $2
  ;; YESTNH-NEXT:   (block (result anyref)
  ;; YESTNH-NEXT:    (call $effect)
  ;; YESTNH-NEXT:    (local.get $ref)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (local.set $3
  ;; YESTNH-NEXT:   (block (result (ref $desc))
  ;; YESTNH-NEXT:    (call $effect)
  ;; YESTNH-NEXT:    (local.get $desc)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (ref.cast (ref none)
  ;; YESTNH-NEXT:   (local.get $2)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $cast-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 anyref)
  ;; NO_TNH-NEXT:  (local $3 (ref $desc))
  ;; NO_TNH-NEXT:  (local.set $2
  ;; NO_TNH-NEXT:   (block (result anyref)
  ;; NO_TNH-NEXT:    (call $effect)
  ;; NO_TNH-NEXT:    (local.get $ref)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (local.set $3
  ;; NO_TNH-NEXT:   (block (result (ref $desc))
  ;; NO_TNH-NEXT:    (call $effect)
  ;; NO_TNH-NEXT:    (local.get $desc)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (ref.cast (ref none)
  ;; NO_TNH-NEXT:   (local.get $2)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $cast-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct))
    ;; Same, but with side effects we cannot drop.
    (ref.cast_desc_eq (ref $struct)
      (block (result anyref)
        (call $effect)
        (local.get $ref)
      )
      (block (result (ref $desc))
        (call $effect)
        (local.get $desc)
      )
    )
  )

  ;; YESTNH:      (func $branch-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (block $block (result nullref)
  ;; YESTNH-NEXT:   (drop
  ;; YESTNH-NEXT:    (block (result (ref any))
  ;; YESTNH-NEXT:     (br_on_cast $block anyref nullref
  ;; YESTNH-NEXT:      (local.get $ref)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (unreachable)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 (ref $desc))
  ;; NO_TNH-NEXT:  (block $block (result nullref)
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (block (result (ref any))
  ;; NO_TNH-NEXT:     (local.set $2
  ;; NO_TNH-NEXT:      (ref.as_non_null
  ;; NO_TNH-NEXT:       (local.get $desc)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (br_on_cast $block anyref nullref
  ;; NO_TNH-NEXT:      (local.get $ref)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $branch-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    (block $block (result (ref null $struct))
      (drop
        ;; Same, but with br_on_cast_desc_eq.
        (br_on_cast_desc_eq $block anyref (ref null $struct)
          (local.get $ref)
          (local.get $desc)
        )
      )
      (unreachable)
    )
  )

  ;; YESTNH:      (func $branch-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (local $2 anyref)
  ;; YESTNH-NEXT:  (local $3 (ref null $desc))
  ;; YESTNH-NEXT:  (block $block (result nullref)
  ;; YESTNH-NEXT:   (drop
  ;; YESTNH-NEXT:    (block (result (ref any))
  ;; YESTNH-NEXT:     (local.set $2
  ;; YESTNH-NEXT:      (block (result anyref)
  ;; YESTNH-NEXT:       (call $effect)
  ;; YESTNH-NEXT:       (local.get $ref)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:     (local.set $3
  ;; YESTNH-NEXT:      (block (result (ref null $desc))
  ;; YESTNH-NEXT:       (call $effect)
  ;; YESTNH-NEXT:       (local.get $desc)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:     (br_on_cast $block anyref nullref
  ;; YESTNH-NEXT:      (local.get $2)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (unreachable)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 anyref)
  ;; NO_TNH-NEXT:  (local $3 (ref $desc))
  ;; NO_TNH-NEXT:  (block $block (result nullref)
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (block (result (ref any))
  ;; NO_TNH-NEXT:     (local.set $2
  ;; NO_TNH-NEXT:      (block (result anyref)
  ;; NO_TNH-NEXT:       (call $effect)
  ;; NO_TNH-NEXT:       (local.get $ref)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (local.set $3
  ;; NO_TNH-NEXT:      (ref.as_non_null
  ;; NO_TNH-NEXT:       (block (result (ref null $desc))
  ;; NO_TNH-NEXT:        (call $effect)
  ;; NO_TNH-NEXT:        (local.get $desc)
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (br_on_cast $block anyref nullref
  ;; NO_TNH-NEXT:      (local.get $2)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $branch-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    (block $block (result (ref null $struct))
      (drop
        ;; Same, but with effects we cannot drop.
        (br_on_cast_desc_eq $block anyref (ref null $struct)
          (block (result anyref)
            (call $effect)
            (local.get $ref)
          )
          (block (result (ref null $desc))
            (call $effect)
            (local.get $desc)
          )
        )
      )
      (unreachable)
    )
  )

  ;; YESTNH:      (func $branch-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (block $block (result (ref none))
  ;; YESTNH-NEXT:   (drop
  ;; YESTNH-NEXT:    (block (result anyref)
  ;; YESTNH-NEXT:     (br_on_cast $block anyref (ref none)
  ;; YESTNH-NEXT:      (local.get $ref)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (unreachable)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 (ref $desc))
  ;; NO_TNH-NEXT:  (block $block (result (ref none))
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (block (result anyref)
  ;; NO_TNH-NEXT:     (local.set $2
  ;; NO_TNH-NEXT:      (ref.as_non_null
  ;; NO_TNH-NEXT:       (local.get $desc)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (br_on_cast $block anyref (ref none)
  ;; NO_TNH-NEXT:      (local.get $ref)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $branch-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    (block $block (result (ref null $struct))
      (drop
        ;; Same, but now the cast does not admit nulls.
        (br_on_cast_desc_eq $block anyref (ref $struct)
          (local.get $ref)
          (local.get $desc)
        )
      )
      (unreachable)
    )
  )

    ;; YESTNH:      (func $branch-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
    ;; YESTNH-NEXT:  (block $block (result (ref none))
    ;; YESTNH-NEXT:   (drop
    ;; YESTNH-NEXT:    (block (result anyref)
    ;; YESTNH-NEXT:     (br_on_cast $block anyref (ref none)
    ;; YESTNH-NEXT:      (local.get $ref)
    ;; YESTNH-NEXT:     )
    ;; YESTNH-NEXT:    )
    ;; YESTNH-NEXT:   )
    ;; YESTNH-NEXT:   (unreachable)
    ;; YESTNH-NEXT:  )
    ;; YESTNH-NEXT: )
    ;; NO_TNH:      (func $branch-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
    ;; NO_TNH-NEXT:  (block $block (result (ref none))
    ;; NO_TNH-NEXT:   (drop
    ;; NO_TNH-NEXT:    (block (result anyref)
    ;; NO_TNH-NEXT:     (br_on_cast $block anyref (ref none)
    ;; NO_TNH-NEXT:      (local.get $ref)
    ;; NO_TNH-NEXT:     )
    ;; NO_TNH-NEXT:    )
    ;; NO_TNH-NEXT:   )
    ;; NO_TNH-NEXT:   (unreachable)
    ;; NO_TNH-NEXT:  )
    ;; NO_TNH-NEXT: )
    (func $branch-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct))
    (block $block (result (ref null $struct))
      (drop
        ;; Same, but now the descriptor is additionally non-null.
        (br_on_cast_desc_eq $block anyref (ref $struct)
          (local.get $ref)
          (local.get $desc)
        )
      )
      (unreachable)
    )
  )

  ;; YESTNH:      (func $branch-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; YESTNH-NEXT:  (local $2 anyref)
  ;; YESTNH-NEXT:  (local $3 (ref $desc))
  ;; YESTNH-NEXT:  (block $block (result (ref none))
  ;; YESTNH-NEXT:   (drop
  ;; YESTNH-NEXT:    (block (result anyref)
  ;; YESTNH-NEXT:     (local.set $2
  ;; YESTNH-NEXT:      (block (result anyref)
  ;; YESTNH-NEXT:       (call $effect)
  ;; YESTNH-NEXT:       (local.get $ref)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:     (local.set $3
  ;; YESTNH-NEXT:      (block (result (ref $desc))
  ;; YESTNH-NEXT:       (call $effect)
  ;; YESTNH-NEXT:       (local.get $desc)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:     (br_on_cast $block anyref (ref none)
  ;; YESTNH-NEXT:      (local.get $2)
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:   (unreachable)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 anyref)
  ;; NO_TNH-NEXT:  (local $3 (ref $desc))
  ;; NO_TNH-NEXT:  (block $block (result (ref none))
  ;; NO_TNH-NEXT:   (drop
  ;; NO_TNH-NEXT:    (block (result anyref)
  ;; NO_TNH-NEXT:     (local.set $2
  ;; NO_TNH-NEXT:      (block (result anyref)
  ;; NO_TNH-NEXT:       (call $effect)
  ;; NO_TNH-NEXT:       (local.get $ref)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (local.set $3
  ;; NO_TNH-NEXT:      (block (result (ref $desc))
  ;; NO_TNH-NEXT:       (call $effect)
  ;; NO_TNH-NEXT:       (local.get $desc)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:     (br_on_cast $block anyref (ref none)
  ;; NO_TNH-NEXT:      (local.get $2)
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:   (unreachable)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $branch-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct))
    (block $block (result (ref null $struct))
      (drop
        ;; Same, but with effects we cannot drop.
        (br_on_cast_desc_eq $block anyref (ref $struct)
          (block (result anyref)
            (call $effect)
            (local.get $ref)
          )
          (block (result (ref $desc))
            (call $effect)
            (local.get $desc)
          )
        )
      )
      (unreachable)
    )
  )

  ;; YESTNH:      (func $branch-fail-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block $block (result (ref any))
  ;; YESTNH-NEXT:    (return
  ;; YESTNH-NEXT:     (block (result nullref)
  ;; YESTNH-NEXT:      (br_on_cast_fail $block anyref nullref
  ;; YESTNH-NEXT:       (local.get $ref)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-fail-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 (ref $desc))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block $block (result (ref any))
  ;; NO_TNH-NEXT:    (return
  ;; NO_TNH-NEXT:     (block (result nullref)
  ;; NO_TNH-NEXT:      (local.set $2
  ;; NO_TNH-NEXT:       (ref.as_non_null
  ;; NO_TNH-NEXT:        (local.get $desc)
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (br_on_cast_fail $block anyref nullref
  ;; NO_TNH-NEXT:       (local.get $ref)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $branch-fail-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    (drop
      (block $block (result anyref)
        (return
          ;; Same, but with br_on_cast_desc_eq_fail.
          (br_on_cast_desc_eq_fail $block anyref (ref null $struct)
            (local.get $ref)
            (local.get $desc)
          )
        )
      )
    )
    (unreachable)
  )

  ;; YESTNH:      (func $branch-fail-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (local $2 anyref)
  ;; YESTNH-NEXT:  (local $3 (ref null $desc))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block $block (result (ref any))
  ;; YESTNH-NEXT:    (return
  ;; YESTNH-NEXT:     (block (result nullref)
  ;; YESTNH-NEXT:      (local.set $2
  ;; YESTNH-NEXT:       (block (result anyref)
  ;; YESTNH-NEXT:        (call $effect)
  ;; YESTNH-NEXT:        (local.get $ref)
  ;; YESTNH-NEXT:       )
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:      (local.set $3
  ;; YESTNH-NEXT:       (block (result (ref null $desc))
  ;; YESTNH-NEXT:        (call $effect)
  ;; YESTNH-NEXT:        (local.get $desc)
  ;; YESTNH-NEXT:       )
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:      (br_on_cast_fail $block anyref nullref
  ;; YESTNH-NEXT:       (local.get $2)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-fail-nullable-effect (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 anyref)
  ;; NO_TNH-NEXT:  (local $3 (ref $desc))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block $block (result (ref any))
  ;; NO_TNH-NEXT:    (return
  ;; NO_TNH-NEXT:     (block (result nullref)
  ;; NO_TNH-NEXT:      (local.set $2
  ;; NO_TNH-NEXT:       (block (result anyref)
  ;; NO_TNH-NEXT:        (call $effect)
  ;; NO_TNH-NEXT:        (local.get $ref)
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (local.set $3
  ;; NO_TNH-NEXT:       (ref.as_non_null
  ;; NO_TNH-NEXT:        (block (result (ref null $desc))
  ;; NO_TNH-NEXT:         (call $effect)
  ;; NO_TNH-NEXT:         (local.get $desc)
  ;; NO_TNH-NEXT:        )
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (br_on_cast_fail $block anyref nullref
  ;; NO_TNH-NEXT:       (local.get $2)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $branch-fail-nullable-effect (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    (drop
      (block $block (result anyref)
        (return
          ;; Same, but with effects.
          (br_on_cast_desc_eq_fail $block anyref (ref null $struct)
            (block (result anyref)
              (call $effect)
              (local.get $ref)
            )
            (block (result (ref null $desc))
              (call $effect)
              (local.get $desc)
            )
          )
        )
      )
    )
    (unreachable)
  )

  ;; YESTNH:      (func $branch-fail-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block $block (result anyref)
  ;; YESTNH-NEXT:    (return
  ;; YESTNH-NEXT:     (block (result (ref none))
  ;; YESTNH-NEXT:      (br_on_cast_fail $block anyref (ref none)
  ;; YESTNH-NEXT:       (local.get $ref)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-fail-non-nullable (type $2) (param $ref anyref) (param $desc (ref null $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 (ref $desc))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block $block (result anyref)
  ;; NO_TNH-NEXT:    (return
  ;; NO_TNH-NEXT:     (block (result (ref none))
  ;; NO_TNH-NEXT:      (local.set $2
  ;; NO_TNH-NEXT:       (ref.as_non_null
  ;; NO_TNH-NEXT:        (local.get $desc)
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (br_on_cast_fail $block anyref (ref none)
  ;; NO_TNH-NEXT:       (local.get $ref)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $branch-fail-non-nullable (param $ref anyref) (param $desc (ref null $desc)) (result (ref null $struct))
    (drop
      (block $block (result anyref)
        (return
          ;; Same, but now without admitting null.
          (br_on_cast_desc_eq_fail $block anyref (ref $struct)
            (local.get $ref)
            (local.get $desc)
          )
        )
      )
    )
    (unreachable)
  )

  ;; YESTNH:      (func $branch-fail-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block $block (result anyref)
  ;; YESTNH-NEXT:    (return
  ;; YESTNH-NEXT:     (block (result (ref none))
  ;; YESTNH-NEXT:      (br_on_cast_fail $block anyref (ref none)
  ;; YESTNH-NEXT:       (local.get $ref)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-fail-non-nullable-desc (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block $block (result anyref)
  ;; NO_TNH-NEXT:    (return
  ;; NO_TNH-NEXT:     (block (result (ref none))
  ;; NO_TNH-NEXT:      (br_on_cast_fail $block anyref (ref none)
  ;; NO_TNH-NEXT:       (local.get $ref)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $branch-fail-non-nullable-desc (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct))
    (drop
      (block $block (result anyref)
        (return
          ;; Same, but now the descriptor is additionally non-null.
          (br_on_cast_desc_eq_fail $block anyref (ref $struct)
            (local.get $ref)
            (local.get $desc)
          )
        )
      )
    )
    (unreachable)
  )

  ;; YESTNH:      (func $branch-fail-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; YESTNH-NEXT:  (local $2 anyref)
  ;; YESTNH-NEXT:  (local $3 (ref $desc))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block $block (result anyref)
  ;; YESTNH-NEXT:    (return
  ;; YESTNH-NEXT:     (block (result (ref none))
  ;; YESTNH-NEXT:      (local.set $2
  ;; YESTNH-NEXT:       (block (result anyref)
  ;; YESTNH-NEXT:        (call $effect)
  ;; YESTNH-NEXT:        (local.get $ref)
  ;; YESTNH-NEXT:       )
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:      (local.set $3
  ;; YESTNH-NEXT:       (block (result (ref $desc))
  ;; YESTNH-NEXT:        (call $effect)
  ;; YESTNH-NEXT:        (local.get $desc)
  ;; YESTNH-NEXT:       )
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:      (br_on_cast_fail $block anyref (ref none)
  ;; YESTNH-NEXT:       (local.get $2)
  ;; YESTNH-NEXT:      )
  ;; YESTNH-NEXT:     )
  ;; YESTNH-NEXT:    )
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $branch-fail-non-nullable-effect (type $3) (param $ref anyref) (param $desc (ref $desc)) (result nullref)
  ;; NO_TNH-NEXT:  (local $2 anyref)
  ;; NO_TNH-NEXT:  (local $3 (ref $desc))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block $block (result anyref)
  ;; NO_TNH-NEXT:    (return
  ;; NO_TNH-NEXT:     (block (result (ref none))
  ;; NO_TNH-NEXT:      (local.set $2
  ;; NO_TNH-NEXT:       (block (result anyref)
  ;; NO_TNH-NEXT:        (call $effect)
  ;; NO_TNH-NEXT:        (local.get $ref)
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (local.set $3
  ;; NO_TNH-NEXT:       (block (result (ref $desc))
  ;; NO_TNH-NEXT:        (call $effect)
  ;; NO_TNH-NEXT:        (local.get $desc)
  ;; NO_TNH-NEXT:       )
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:      (br_on_cast_fail $block anyref (ref none)
  ;; NO_TNH-NEXT:       (local.get $2)
  ;; NO_TNH-NEXT:      )
  ;; NO_TNH-NEXT:     )
  ;; NO_TNH-NEXT:    )
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $branch-fail-non-nullable-effect (param $ref anyref) (param $desc (ref $desc)) (result (ref null $struct))
    (drop
      (block $block (result anyref)
        (return
          ;; Same, but with effects.
          (br_on_cast_desc_eq_fail $block anyref (ref $struct)
            (block (result anyref)
              (call $effect)
              (local.get $ref)
            )
            (block (result (ref $desc))
              (call $effect)
              (local.get $desc)
            )
          )
        )
      )
    )
    (unreachable)
  )
)

(module
  ;; We will optimize the descriptor type, so we must take care not to leave
  ;; invalid allocations of its described type.
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $struct (sub (descriptor $uninstantiated) (struct)))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $struct (sub (descriptor $uninstantiated) (struct)))
    (type $struct (sub (descriptor $uninstantiated) (struct)))
    ;; YESTNH:       (type $uninstantiated (sub (describes $struct) (struct)))
    ;; NO_TNH:       (type $uninstantiated (sub (describes $struct) (struct)))
    (type $uninstantiated (sub (describes $struct) (struct)))
    ;; YESTNH:       (type $sub (sub $struct (descriptor $instantiated) (struct)))
    ;; NO_TNH:       (type $sub (sub $struct (descriptor $instantiated) (struct)))
    (type $sub (sub $struct (descriptor $instantiated) (struct)))
    ;; YESTNH:       (type $instantiated (sub $uninstantiated (describes $sub) (struct)))
    ;; NO_TNH:       (type $instantiated (sub $uninstantiated (describes $sub) (struct)))
    (type $instantiated (sub $uninstantiated (describes $sub) (struct)))
  )

  ;; YESTNH:       (type $4 (func (result (ref $struct))))

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

  ;; YESTNH:      (import "" "" (func $effect (type $5)))
  ;; NO_TNH:       (type $4 (func (result (ref $struct))))

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

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

  ;; YESTNH:      (global $instantiated (ref $instantiated) (struct.new_default $instantiated))
  ;; NO_TNH:      (global $instantiated (ref $instantiated) (struct.new_default $instantiated))
  (global $instantiated (ref $instantiated) (struct.new $instantiated))

  ;; YESTNH:      (global $fake-desc (ref null (exact $instantiated)) (ref.null none))
  ;; NO_TNH:      (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none))
  (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none))

  ;; YESTNH:      (global $impossible (ref $struct) (struct.new_default_desc $struct
  ;; YESTNH-NEXT:  (ref.null none)
  ;; YESTNH-NEXT: ))
  ;; NO_TNH:      (global $impossible (ref $struct) (struct.new_default_desc $struct
  ;; NO_TNH-NEXT:  (global.get $fake-desc)
  ;; NO_TNH-NEXT: ))
  (global $impossible (ref $struct)
    (struct.new_desc $struct
      (global.get $fake-desc)
    )
  )

  ;; YESTNH:      (func $impossible (type $4) (result (ref $struct))
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $impossible (type $4) (result (ref $struct))
  ;; NO_TNH-NEXT:  (struct.new_default_desc $struct
  ;; NO_TNH-NEXT:   (global.get $fake-desc)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $impossible (result (ref $struct))
    (struct.new_desc $struct
      (global.get $fake-desc)
    )
  )

  ;; YESTNH:      (func $impossible-effect (type $4) (result (ref $struct))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block (result (ref null (exact $instantiated)))
  ;; YESTNH-NEXT:    (call $effect)
  ;; YESTNH-NEXT:    (global.get $fake-desc)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $impossible-effect (type $4) (result (ref $struct))
  ;; NO_TNH-NEXT:  (struct.new_default_desc $struct
  ;; NO_TNH-NEXT:   (block (result (ref null (exact $uninstantiated)))
  ;; NO_TNH-NEXT:    (call $effect)
  ;; NO_TNH-NEXT:    (global.get $fake-desc)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $impossible-effect (result (ref $struct))
    (struct.new_desc $struct
      (block (result (ref null (exact $uninstantiated)))
        (call $effect)
        (global.get $fake-desc)
      )
    )
  )
)

(module
  ;; Same, but now we're optimizing the descriptor to bottom, so we don't need
  ;; to do any preoptimization to ensure validity. We optimize anyway because
  ;; we can.
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $struct (descriptor $uninstantiated) (struct))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $struct (descriptor $uninstantiated) (struct))
    (type $struct (descriptor $uninstantiated) (struct))
    ;; YESTNH:       (type $uninstantiated (describes $struct) (struct))
    ;; NO_TNH:       (type $uninstantiated (describes $struct) (struct))
    (type $uninstantiated (describes $struct) (struct))
  )

  ;; YESTNH:       (type $2 (func (result (ref $struct))))

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

  ;; YESTNH:      (import "" "" (func $effect (type $3)))
  ;; NO_TNH:       (type $2 (func (result (ref $struct))))

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

  ;; NO_TNH:      (import "" "" (func $effect (type $3)))
  (import "" "" (func $effect))

  ;; YESTNH:      (global $fake-desc nullref (ref.null none))
  ;; NO_TNH:      (global $fake-desc nullref (ref.null none))
  (global $fake-desc (ref null (exact $uninstantiated)) (ref.null none))

  ;; YESTNH:      (global $impossible (ref $struct) (struct.new_default_desc $struct
  ;; YESTNH-NEXT:  (ref.null none)
  ;; YESTNH-NEXT: ))
  ;; NO_TNH:      (global $impossible (ref $struct) (struct.new_default_desc $struct
  ;; NO_TNH-NEXT:  (ref.null none)
  ;; NO_TNH-NEXT: ))
  (global $impossible (ref $struct)
    (struct.new_desc $struct
      (global.get $fake-desc)
    )
  )

  ;; YESTNH:      (func $impossible (type $2) (result (ref $struct))
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $impossible (type $2) (result (ref $struct))
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $impossible (result (ref $struct))
    (struct.new_desc $struct
      (global.get $fake-desc)
    )
  )

  ;; YESTNH:      (func $impossible-effect (type $2) (result (ref $struct))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (block (result nullref)
  ;; YESTNH-NEXT:    (call $effect)
  ;; YESTNH-NEXT:    (global.get $fake-desc)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $impossible-effect (type $2) (result (ref $struct))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (block (result nullref)
  ;; NO_TNH-NEXT:    (call $effect)
  ;; NO_TNH-NEXT:    (global.get $fake-desc)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $impossible-effect (result (ref $struct))
    (struct.new_desc $struct
      (block (result (ref null (exact $uninstantiated)))
        (call $effect)
        (global.get $fake-desc)
      )
    )
  )
)

(module
  ;; $B is never created. When removing it from the result of the function, we
  ;; must remove the ref.get_desc too (otherwise it would return a non-
  ;; validating type for the new result; and it traps anyhow).
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $A (sub (descriptor $B) (struct)))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $A (sub (descriptor $B) (struct)))
    (type $A (sub (descriptor $B) (struct)))
    ;; YESTNH:       (type $B (describes $A) (struct))
    ;; NO_TNH:       (type $B (describes $A) (struct))
    (type $B (describes $A) (struct))
  )

  ;; YESTNH:       (type $2 (func (result (ref none))))

  ;; YESTNH:       (type $3 (func (param (ref $A)) (result (ref none))))

  ;; YESTNH:      (func $test (type $2) (result (ref none))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (struct.new_default_desc $A
  ;; YESTNH-NEXT:    (ref.null none)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:       (type $2 (func (result (ref none))))

  ;; NO_TNH:       (type $3 (func (param (ref $A)) (result (ref none))))

  ;; NO_TNH:      (func $test (type $2) (result (ref none))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (struct.new_default_desc $A
  ;; NO_TNH-NEXT:    (ref.null none)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $test (result (ref (exact $B)))
    (ref.get_desc $A
      (struct.new_default_desc $A
        (ref.null none)
      )
    )
  )

  ;; YESTNH:      (func $inexact (type $3) (param $inexact (ref $A)) (result (ref none))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (local.get $inexact)
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $inexact (type $3) (param $inexact (ref $A)) (result (ref none))
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (local.get $inexact)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT:  (unreachable)
  ;; NO_TNH-NEXT: )
  (func $inexact (param $inexact (ref $A)) (result (ref $B))
    ;; As above, but now the input type is inexact. There are no subtypes, so
    ;; we still optimize.
    (ref.get_desc $A
      (local.get $inexact)
    )
  )
)

(module
  ;; As above, but now there are subtypes $A.sub, $B.sub.
  (rec
    ;; YESTNH:      (rec
    ;; YESTNH-NEXT:  (type $A (sub (descriptor $B) (struct)))
    ;; NO_TNH:      (rec
    ;; NO_TNH-NEXT:  (type $A (sub (descriptor $B) (struct)))
    (type $A (sub (descriptor $B) (struct)))
    ;; YESTNH:       (type $B (sub (describes $A) (struct)))
    ;; NO_TNH:       (type $B (sub (describes $A) (struct)))
    (type $B (sub (describes $A) (struct)))

    ;; YESTNH:       (type $A.sub (sub $A (descriptor $B.sub) (struct)))
    ;; NO_TNH:       (type $A.sub (sub $A (descriptor $B.sub) (struct)))
    (type $A.sub (sub $A (descriptor $B.sub) (struct)))
    ;; YESTNH:       (type $B.sub (sub $B (describes $A.sub) (struct)))
    ;; NO_TNH:       (type $B.sub (sub $B (describes $A.sub) (struct)))
    (type $B.sub (sub $B (describes $A.sub) (struct)))
  )

  ;; YESTNH:       (type $4 (func))

  ;; YESTNH:       (type $5 (func (result (ref (exact $B.sub)))))

  ;; YESTNH:       (type $6 (func (param (ref $A)) (result (ref $B.sub))))

  ;; YESTNH:      (func $create (type $4)
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (struct.new_default_desc $A.sub
  ;; YESTNH-NEXT:    (struct.new_default $B.sub)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:       (type $4 (func))

  ;; NO_TNH:       (type $5 (func (result (ref (exact $B)))))

  ;; NO_TNH:       (type $6 (func (param (ref $A)) (result (ref $B))))

  ;; NO_TNH:      (func $create (type $4)
  ;; NO_TNH-NEXT:  (drop
  ;; NO_TNH-NEXT:   (struct.new_default_desc $A.sub
  ;; NO_TNH-NEXT:    (struct.new_default $B.sub)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $create
    ;; Make the subtypes not abstract.
    (drop
      (struct.new_default_desc $A.sub
        (struct.new_default $B.sub)
      )
    )
  )

  ;; YESTNH:      (func $test (type $5) (result (ref (exact $B.sub)))
  ;; YESTNH-NEXT:  (drop
  ;; YESTNH-NEXT:   (struct.new_default_desc $A
  ;; YESTNH-NEXT:    (ref.null none)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT:  (unreachable)
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $test (type $5) (result (ref (exact $B)))
  ;; NO_TNH-NEXT:  (ref.get_desc $A
  ;; NO_TNH-NEXT:   (struct.new_default_desc $A
  ;; NO_TNH-NEXT:    (ref.null none)
  ;; NO_TNH-NEXT:   )
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $test (result (ref (exact $B)))
    ;; We can still optimize here, thanks to exactness (when TNH).
    (ref.get_desc $A
      (struct.new_default_desc $A
        (ref.null none)
      )
    )
  )

  ;; YESTNH:      (func $inexact (type $6) (param $inexact (ref $A)) (result (ref $B.sub))
  ;; YESTNH-NEXT:  (ref.get_desc $A.sub
  ;; YESTNH-NEXT:   (ref.cast (ref $A.sub)
  ;; YESTNH-NEXT:    (local.get $inexact)
  ;; YESTNH-NEXT:   )
  ;; YESTNH-NEXT:  )
  ;; YESTNH-NEXT: )
  ;; NO_TNH:      (func $inexact (type $6) (param $inexact (ref $A)) (result (ref $B))
  ;; NO_TNH-NEXT:  (ref.get_desc $A
  ;; NO_TNH-NEXT:   (local.get $inexact)
  ;; NO_TNH-NEXT:  )
  ;; NO_TNH-NEXT: )
  (func $inexact (param $inexact (ref $A)) (result (ref $B))
    ;; We do not optimize to an unreachable here, as the inexact reference may
    ;; contain an $A.sub. We optimize to that, using a cast (in TNH).
    (ref.get_desc $A
      (local.get $inexact)
    )
  )
)

;; Shared abstract described types.
(module
 (rec
  (type $A (sub (shared (descriptor $B) (struct))))
  (type $B (sub (shared (describes $A) (descriptor $C) (struct))))
  (type $C (sub (shared (describes $B) (struct))))
 )

 ;; YESTNH:      (rec
 ;; YESTNH-NEXT:  (type $0 (func (param (ref (shared none))) (result (ref (shared none)))))

 ;; YESTNH:       (type $1 (func (param (ref (shared none))) (result (ref (shared none)))))

 ;; YESTNH:      (func $A (type $0) (param $x (ref (shared none))) (result (ref (shared none)))
 ;; YESTNH-NEXT:  (drop
 ;; YESTNH-NEXT:   (local.get $x)
 ;; YESTNH-NEXT:  )
 ;; YESTNH-NEXT:  (unreachable)
 ;; YESTNH-NEXT: )
 ;; NO_TNH:      (rec
 ;; NO_TNH-NEXT:  (type $0 (func (param (ref (shared none))) (result (ref (shared none)))))

 ;; NO_TNH:       (type $1 (func (param (ref (shared none))) (result (ref (shared none)))))

 ;; NO_TNH:      (func $A (type $0) (param $x (ref (shared none))) (result (ref (shared none)))
 ;; NO_TNH-NEXT:  (drop
 ;; NO_TNH-NEXT:   (local.get $x)
 ;; NO_TNH-NEXT:  )
 ;; NO_TNH-NEXT:  (unreachable)
 ;; NO_TNH-NEXT: )
 (func $A (param $x (ref $A)) (result (ref $B))
  (ref.get_desc $A
   (local.get $x)
  )
 )

 ;; YESTNH:      (func $B (type $1) (param $x (ref (shared none))) (result (ref (shared none)))
 ;; YESTNH-NEXT:  (drop
 ;; YESTNH-NEXT:   (local.get $x)
 ;; YESTNH-NEXT:  )
 ;; YESTNH-NEXT:  (unreachable)
 ;; YESTNH-NEXT: )
 ;; NO_TNH:      (func $B (type $1) (param $x (ref (shared none))) (result (ref (shared none)))
 ;; NO_TNH-NEXT:  (drop
 ;; NO_TNH-NEXT:   (local.get $x)
 ;; NO_TNH-NEXT:  )
 ;; NO_TNH-NEXT:  (unreachable)
 ;; NO_TNH-NEXT: )
 (func $B (param $x (ref $B)) (result (ref $C))
  (ref.get_desc $B
   (local.get $x)
  )
 )
)
