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

;; RUN: foreach %s %t wasm-opt -all --closed-world --preserve-type-order \
;; RUN:     --type-merging --remove-unused-types -S -o - | filecheck %s

;; Check that types that differ only in exactness are not merged.
(module
 ;; CHECK:      (rec
 ;; CHECK-NEXT:  (type $foo (struct))
 (type $foo (struct))
 ;; CHECK:       (type $A (struct (field (ref (exact $foo)))))
 (type $A (struct (field (ref (exact $foo)))))
 ;; CHECK:       (type $B (struct (field (ref $foo))))
 (type $B (struct (field (ref $foo))))

 ;; CHECK:      (global $a (ref null $A) (ref.null none))
 (global $a (ref null $A) (ref.null none))
 ;; CHECK:      (global $b (ref null $B) (ref.null none))
 (global $b (ref null $B) (ref.null none))
)

;; Check that exact casts to a supertype prevent subtypes from being merged into
;; it.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $super (sub (struct)))
  (type $super (sub (struct)))
  ;; CHECK:       (type $sub (sub $super (struct)))
  (type $sub (sub $super (struct)))

  ;; CHECK:      (func $ref-cast (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (local $sub (ref null $sub))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.cast (ref (exact $super))
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-cast (param $any anyref)
    (local $sub (ref null $sub))
    (drop
      (ref.cast (ref (exact $super))
        (local.get $any)
      )
    )
  )
)

;; Same as above but with ref.test.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $super (sub (struct)))
  (type $super (sub (struct)))
  ;; CHECK:       (type $sub (sub $super (struct)))
  (type $sub (sub $super (struct)))

  ;; CHECK:      (func $ref-test (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (local $sub (ref null $sub))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (ref.test (ref (exact $super))
  ;; CHECK-NEXT:    (local.get $any)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $ref-test (param $any anyref)
    (local $sub (ref null $sub))
    (drop
      (ref.test (ref (exact $super))
        (local.get $any)
      )
    )
  )
)

;; Same as above but with br_on_cast.
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $super (sub (struct)))
  (type $super (sub (struct)))
  ;; CHECK:       (type $sub (sub $super (struct)))
  (type $sub (sub $super (struct)))

  ;; CHECK:      (func $br-on-cast (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (local $sub (ref null $sub))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $l (result anyref)
  ;; CHECK-NEXT:    (br_on_cast $l anyref (ref (exact $super))
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast (param $any anyref)
    (local $sub (ref null $sub))
    (drop
      (block $l (result anyref)
        (br_on_cast $l anyref (ref (exact $super))
          (local.get $any)
        )
      )
    )
  )
)

;; Same as above but with br_on_cast_fail
(module
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $super (sub (struct)))
  (type $super (sub (struct)))
  ;; CHECK:       (type $sub (sub $super (struct)))
  (type $sub (sub $super (struct)))

  ;; CHECK:      (func $br-on-cast-fail (type $2) (param $any anyref)
  ;; CHECK-NEXT:  (local $sub (ref null $sub))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (block $l (result anyref)
  ;; CHECK-NEXT:    (br_on_cast_fail $l anyref (ref (exact $super))
  ;; CHECK-NEXT:     (local.get $any)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $br-on-cast-fail (param $any anyref)
    (local $sub (ref null $sub))
    (drop
      (block $l (result anyref)
        (br_on_cast_fail $l anyref (ref (exact $super))
          (local.get $any)
        )
      )
    )
  )
)

;; Merge two siblings to their parent. We must refinalize here.
(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $A (sub (array i8)))
    (type $A (sub (array i8)))
    (type $B1 (sub $A (array i8)))
    (type $B2 (sub $A (array i8)))
  )

  ;; CHECK:      (func $test (type $1)
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (select (result (ref (exact $A)))
  ;; CHECK-NEXT:    (array.new_default $A
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (array.new_default $A
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:    (i32.const 2)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    ;; B1 and B2 will turn into A, after which the select can be refined to an
    ;; exact type.
    (drop
      (select (result (ref $A))
        (array.new_default $B1
          (i32.const 0)
        )
        (array.new_default $B2
          (i32.const 1)
        )
        (i32.const 2)
      )
    )
  )
)

