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

;; RUN: foreach %s %t wasm-opt -all --closed-world --gto --preserve-type-order -S -o - | filecheck %s

(module
  (rec
    ;; CHECK:      (rec
    ;; CHECK-NEXT:  (type $struct (descriptor $desc) (struct (field i32)))
    (type $struct (descriptor $desc) (struct (field i32)))
    ;; CHECK:       (type $desc (describes $struct) (struct))
    (type $desc (describes $struct) (struct))
    ;; CHECK:       (type $pair (struct))
    (type $pair (struct (field (ref $struct)) (field (ref $struct))))
    ;; CHECK:       (type $used-pair (struct (field (ref $struct)) (field (ref $struct))))
    (type $used-pair (struct (field (ref $struct)) (field (ref $struct))))
  )

  ;; CHECK:       (type $4 (func (param (ref $struct) (ref $used-pair))))

  ;; CHECK:      (global $nullable-desc (ref null (exact $desc)) (struct.new_default $desc))
  (global $nullable-desc (ref null (exact $desc)) (struct.new $desc))

  ;; Check that we generate fresh names for added globals.
  ;; CHECK:      (global $gto-removed-1 nullref (ref.null none))
  (global $gto-removed-1 nullref (ref.null none))

  ;; CHECK:      (global $second-traps (ref $pair) (struct.new_default $pair))
  (global $second-traps (ref $pair)
    ;; Both fields will be removed, but only the second can trap, so only the
    ;; second will be moved to a new global.
    (struct.new $pair
      (struct.new_desc $struct
        (i32.const 0)
        (struct.new $desc)
      )
      (struct.new_desc $struct
        (i32.const 1)
        (ref.null none)
      )
    )
  )

  ;; CHECK:      (global $first-traps (ref $pair) (struct.new_default $pair))
  (global $first-traps (ref $pair)
    ;; Same as above, but now the first traps (or at least we assume it can
    ;; based on its type).
    (struct.new $pair
      (struct.new_desc $struct
        (i32.const 2)
        (global.get $nullable-desc)
      )
      (struct.new_desc $struct
        (i32.const 3)
        (struct.new $desc)
      )
    )
  )

  ;; CHECK:      (global $used-traps (ref $used-pair) (struct.new $used-pair
  ;; CHECK-NEXT:  (struct.new_desc $struct
  ;; CHECK-NEXT:   (i32.const 4)
  ;; CHECK-NEXT:   (ref.null none)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (struct.new_desc $struct
  ;; CHECK-NEXT:   (i32.const 5)
  ;; CHECK-NEXT:   (ref.null none)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: ))
  (global $used-traps (ref $used-pair)
    ;; Now both trap, but they are also used, so they will not be removed and no
    ;; globals will be created.
    (struct.new $used-pair
      (struct.new_desc $struct
        (i32.const 4)
        (ref.null none)
      )
      (struct.new_desc $struct
        (i32.const 5)
        (ref.null none)
      )
    )
  )

  ;; CHECK:      (global $gto-removed-0 (ref (exact $struct)) (struct.new_desc $struct
  ;; CHECK-NEXT:  (i32.const 1)
  ;; CHECK-NEXT:  (ref.null none)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (global $gto-removed-1_6 (ref (exact $struct)) (struct.new_desc $struct
  ;; CHECK-NEXT:  (i32.const 2)
  ;; CHECK-NEXT:  (global.get $nullable-desc)
  ;; CHECK-NEXT: ))

  ;; CHECK:      (func $use-struct-fields (type $4) (param $0 (ref $struct)) (param $1 (ref $used-pair))
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $struct 0
  ;; CHECK-NEXT:    (local.get $0)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $used-pair 0
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (drop
  ;; CHECK-NEXT:   (struct.get $used-pair 1
  ;; CHECK-NEXT:    (local.get $1)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $use-struct-fields (param (ref $struct)) (param (ref $used-pair))
    ;; Prevent the i32s in the initializers from being removed.
    (drop
      (struct.get $struct 0
        (local.get 0)
      )
    )
    ;; Prevent the fields in used-pair from being removed.
    (drop
      (struct.get $used-pair 0
        (local.get 1)
      )
    )
    (drop
      (struct.get $used-pair 1
        (local.get 1)
      )
    )
  )
)

;; The descriptor here is not needed, but we now optimize descriptors in
;; Unsubtyping, so we do nothing here.
(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 $2 (func))

  ;; CHECK:      (func $test (type $2)
  ;; CHECK-NEXT:  (local $A (ref $A))
  ;; CHECK-NEXT:  (local $B (ref $B))
  ;; CHECK-NEXT: )
  (func $test
    (local $A (ref $A))
    (local $B (ref $B))
  )
)


