;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
;; RUN: foreach %s %t wasm-opt --remove-unused-module-elements --closed-world -all -S -o - | filecheck %s

;; The global.get of $global is the descriptor, not a field of the struct.
;; We should not try to optimize it away and error.
(module
 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $struct (descriptor $desc) (struct))
  (type $struct (descriptor $desc) (struct))
  ;; CHECK:       (type $desc (describes $struct) (struct))
  (type $desc (describes $struct) (struct))
 )

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

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

 ;; CHECK:      (export "export" (func $export))

 ;; CHECK:      (func $export (type $2)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (struct.new_default_desc $struct
 ;; CHECK-NEXT:    (global.get $desc)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $export (export "export")
  (drop
   (struct.new_desc $struct
    (global.get $desc)
   )
  )
 )
)

;; We cannot optimize out globals whose initializers might trap due to a null
;; descriptor (or conservatively even just a nullable descriptor).
(module
 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $struct (descriptor $desc) (struct))
  (type $struct (descriptor $desc) (struct))
  ;; CHECK:       (type $desc (describes $struct) (struct))
  (type $desc (describes $struct) (struct))
  ;; CHECK:       (type $list (struct (field (ref $struct)) (field (ref null $list))))
  (type $list (struct (field (ref $struct)) (field (ref null $list))))
 )

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

 ;; Trapping globals must be kept, but non-trapping globals can be removed.
 ;; CHECK:      (global $trap (ref $struct) (struct.new_default_desc $struct
 ;; CHECK-NEXT:  (ref.null none)
 ;; CHECK-NEXT: ))
 (global $trap (ref $struct) (struct.new_desc $struct (ref.null none)))
 (global $no-trap (ref $struct) (struct.new_desc $struct (struct.new $desc)))

 ;; CHECK:      (global $trap-get-null (ref $struct) (struct.new_default_desc $struct
 ;; CHECK-NEXT:  (global.get $null)
 ;; CHECK-NEXT: ))
 (global $trap-get-null (ref $struct) (struct.new_desc $struct (global.get $null)))
 ;; CHECK:      (global $trap-get-nullable (ref $struct) (struct.new_default_desc $struct
 ;; CHECK-NEXT:  (global.get $nullable-desc)
 ;; CHECK-NEXT: ))
 (global $trap-get-nullable (ref $struct) (struct.new_desc $struct (global.get $nullable-desc)))
 (global $no-trap-get (ref $struct) (struct.new_desc $struct (global.get $desc)))

 ;; CHECK:      (global $trap-nested (ref $list) (struct.new $list
 ;; CHECK-NEXT:  (struct.new_default_desc $struct
 ;; CHECK-NEXT:   (struct.new_default $desc)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (struct.new $list
 ;; CHECK-NEXT:   (struct.new_default_desc $struct
 ;; CHECK-NEXT:    (global.get $desc)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:   (struct.new $list
 ;; CHECK-NEXT:    (struct.new_default_desc $struct
 ;; CHECK-NEXT:     (ref.null none)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (ref.null none)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: ))
 (global $trap-nested (ref $list)
  (struct.new $list
   (struct.new_desc $struct (struct.new $desc))
   (struct.new $list
    (struct.new_desc $struct (global.get $desc))
    (struct.new $list
     (struct.new_desc $struct (ref.null none))
     (ref.null none)
    )
   )
  )
 )

 (global $no-trap-nested (ref $list)
  (struct.new $list
   (struct.new_desc $struct (struct.new $desc))
   (struct.new $list
    (struct.new_desc $struct (global.get $desc))
    (ref.null none)
   )
  )
 )

 ;; CHECK:      (elem $trap anyref (item (struct.new_default_desc $struct
 ;; CHECK-NEXT:  (ref.null none)
 ;; CHECK-NEXT: )))
 (elem $trap anyref (item (struct.new_desc $struct (ref.null none))))
 (elem $no-trap anyref (item (struct.new_desc $struct (struct.new $desc))))
  ;; CHECK:      (elem $trap-get-null anyref (item (struct.new_default_desc $struct
  ;; CHECK-NEXT:  (global.get $null)
  ;; CHECK-NEXT: )))
  (elem $trap-get-null anyref (item (struct.new_desc $struct (global.get $null))))
 ;; CHECK:      (elem $trap-get-nullable anyref (item (struct.new_default_desc $struct
 ;; CHECK-NEXT:  (global.get $nullable-desc)
 ;; CHECK-NEXT: )))
 (elem $trap-get-nullable anyref (item (struct.new_desc $struct (global.get $nullable-desc))))
 (elem $no-trap-get anyref (item (struct.new_desc $struct (global.get $desc))))
)

(module
 ;; CHECK:      (type $void (func))
 (type $void (func))

 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $vtable (sub (descriptor $vtable.desc) (struct (field (ref $void)))))
  (type $vtable (sub (descriptor $vtable.desc) (struct (field (ref $void)))))
  ;; CHECK:       (type $vtable.desc (sub (describes $vtable) (struct (field (ref $void)))))
  (type $vtable.desc (sub (describes $vtable) (struct (field (ref $void)))))
 )

 ;; CHECK:      (global $vtable (ref $vtable) (struct.new_desc $vtable
 ;; CHECK-NEXT:  (ref.func $a)
 ;; CHECK-NEXT:  (struct.new $vtable.desc
 ;; CHECK-NEXT:   (ref.func $b)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: ))
 (global $vtable (ref $vtable) (struct.new_desc $vtable
  (ref.func $a)
  (struct.new $vtable.desc
   (ref.func $b)
  )
 ))

 ;; CHECK:      (export "export" (func $export))

 ;; CHECK:      (func $export (type $void)
 ;; CHECK-NEXT:  (call_ref $void
 ;; CHECK-NEXT:   (struct.get $vtable 0
 ;; CHECK-NEXT:    (global.get $vtable)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $export (export "export")
  ;; Read $a and call it. $b, in the descriptor, should not be callable.
  (call_ref $void
   (struct.get $vtable 0
    (global.get $vtable)
   )
  )
 )

 ;; CHECK:      (func $a (type $void)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (i32.const 42)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $a (type $void)
  ;; This is reached from above.
  (drop (i32.const 42))
 )

 ;; CHECK:      (func $b (type $void)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 (func $b (type $void)
  ;; This is not reached: We never read the descriptor, so we never read field 0
  ;; in it, leaving this as dead (in closed world). That it itself seems to read
  ;; the descriptor should not confuse us.
  (call_ref $void
   (struct.get $vtable.desc 0
    (ref.get_desc $vtable
     (global.get $vtable)
    )
   )
  )
 )
)

