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

;; RUN: foreach %s %t wasm-opt --cfp-reftest --closed-world -all -S -o - | filecheck %s

;; When a ref.get_desc can only read from two types, and those types have a
;; constant field, we can select between those two values using a ref.test.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (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 $A (sub $super (descriptor $A.desc) (struct)))
    (type $A (sub $super (descriptor $A.desc) (struct)))
    ;; CHECK:       (type $A.desc (sub $super.desc (describes $A) (struct)))
    (type $A.desc (sub $super.desc (describes $A) (struct)))

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


  ;; CHECK:      (type $6 (func (param (ref null $super) (ref null $A) (ref null $B))))

  ;; CHECK:      (global $A.desc (ref (exact $A.desc)) (struct.new_default $A.desc))
  (global $A.desc (ref (exact $A.desc)) (struct.new $A.desc))

  ;; CHECK:      (global $B.desc (ref (exact $B.desc)) (struct.new_default $B.desc))
  (global $B.desc (ref (exact $B.desc)) (struct.new $B.desc))

  ;; CHECK:      (func $test (type $6) (param $super (ref null $super)) (param $A (ref null $A)) (param $B (ref null $B))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new_default_desc $A
  ;; CHECK-NEXT:    (global.get $A.desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new_default_desc $B
  ;; CHECK-NEXT:    (global.get $B.desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select (result (ref $super.desc))
  ;; CHECK-NEXT:    (global.get $A.desc)
  ;; CHECK-NEXT:    (global.get $B.desc)
  ;; CHECK-NEXT:    (ref.test (ref $A)
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $super)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result (ref (exact $A.desc)))
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $A)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $A.desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block (result (ref (exact $B.desc)))
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (ref.as_non_null
  ;; CHECK-NEXT:      (local.get $B)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (global.get $B.desc)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (param $super (ref null $super)) (param $A (ref null $A)) (param $B (ref null $B))
    (drop
      (struct.new_desc $A
        (global.get $A.desc)
      )
    )
    (drop
      (struct.new_desc $B
        (global.get $B.desc)
      )
    )
    ;; We can optimize the read from the super here, using a ref.test.
    (drop
      (ref.get_desc $super
        (local.get $super)
      )
    )
    ;; These are optimizable even without ref.test.
    (drop
      (ref.get_desc $A
        (local.get $A)
      )
    )
    (drop
      (ref.get_desc $B
        (local.get $B)
      )
    )
  )
)

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (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 $func (func (param i32) (result i32)))
    (type $func (func (param i32) (result i32)))

    ;; 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 $5 (func (result (ref (exact $super.desc)))))

  ;; CHECK:      (global $A (ref (exact $super.desc)) (struct.new_default $super.desc))
  (global $A (ref (exact $super.desc)) (struct.new $super.desc))

  ;; CHECK:      (global $B (ref (exact $sub.desc)) (struct.new_default $sub.desc))
  (global $B (ref (exact $sub.desc)) (struct.new $sub.desc))

  ;; CHECK:      (func $test (type $5) (result (ref (exact $super.desc)))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new_default_desc $super
  ;; CHECK-NEXT:    (global.get $A)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.new_default_desc $sub
  ;; CHECK-NEXT:    (global.get $B)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (block (result (ref (exact $super.desc)))
  ;; CHECK-NEXT:   (drop
  ;; CHECK-NEXT:    (ref.as_non_null
  ;; CHECK-NEXT:     (block (result nullref)
  ;; CHECK-NEXT:      (ref.null none)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (global.get $A)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test (result (ref (exact $super.desc)))
    (drop
      (struct.new_default_desc $super
        (global.get $A)
      )
    )
    (drop
      (struct.new_default_desc $sub
        (global.get $B)
      )
    )
    ;; We read from an exact $super here, so the type of the ref.get_desc is
    ;; exact as well. If we ignore that in the optimization, we might think that
    ;; the two struct.news before us are two possible values, one from $super and
    ;; one from $sub, and if we emitted a ref.test between those values, we'd get
    ;; a non-exact value that does not validate.
    ;;
    ;; Instead, we should look only at $super itself, and optimize to $A.
    (ref.get_desc $super
      (block (result (ref null (exact $super)))
        (ref.null $super)
      )
    )
  )

  ;; CHECK:      (func $func (type $func) (param $0 i32) (result i32)
  ;; CHECK-NEXT:  (i32.const 42)
  ;; CHECK-NEXT: )
  (func $func (type $func) (param $0 i32) (result i32)
    (i32.const 42)
  )
)
