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

(module
  ;; CHECK:      (type $f (func (param i32)))
  (type $f (func (param i32)))
  ;; CHECK:      (type $no-funcs (func (param i64)))
  (type $no-funcs (func (param i64)))

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

  ;; CHECK:      (type $3 (func (param i32 i64 (ref $no-funcs))))

  ;; CHECK:      (table $t 1 1 funcref)
  (table $t funcref (elem $referenced))

  ;; CHECK:      (elem $implicit-elem (i32.const 0) $referenced)

  ;; CHECK:      (func $referenced (type $f) (param $unused i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $referenced (type $f) (param $unused i32)
    ;; Even though the parameter is not used in any use of this function type,
    ;; we cannot optimize indirect calls in an open world.
    (nop)
  )

  ;; CHECK:      (func $not-referenced (type $2)
  ;; CHECK-NEXT:  (local $unused i32)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $not-referenced (type $f) (param $unused i32)
    ;; Non-referenced functions with the same types can still be optimized.
    (nop)
  )

  ;; CHECK:      (func $caller (type $3) (param $i32 i32) (param $i64 i64) (param $func (ref $no-funcs))
  ;; CHECK-NEXT:  (call_indirect $t (type $f)
  ;; CHECK-NEXT:   (local.get $i32)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $f
  ;; CHECK-NEXT:   (local.get $i32)
  ;; CHECK-NEXT:   (ref.func $referenced)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT:  (call_ref $no-funcs
  ;; CHECK-NEXT:   (local.get $i64)
  ;; CHECK-NEXT:   (local.get $func)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $caller (param $i32 i32) (param $i64 i64) (param $func (ref $no-funcs))
    ;; Since the param remains used, we should not optimize indirect calls or
    ;; call_refs and we should not optimize out the forwarded params.
    (call_indirect (type $f)
      (local.get $i32)
      (i32.const 0)
    )
    (call_ref $f
      (local.get $i32)
      (ref.func $referenced)
    )
    ;; Even function types with no functions cannot be optimized because in
    ;; an open world there might be external functions with the type.
    (call_ref $no-funcs
      (local.get $i64)
      (local.get $func)
    )
  )
)

(module
  ;; CHECK:      (type $f (func (param i32 i64)))
  (type $f (func (param i32 i64)))
  ;; CHECK:      (type $1 (func))

  ;; CHECK:      (func $test (type $1)
  ;; CHECK-NEXT:  (local $f (ref null $f))
  ;; CHECK-NEXT:  (call_ref $f
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:   (i64.const 1)
  ;; CHECK-NEXT:   (local.get $f)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $test
    ;; There is no function with type $f, so we can normally remove all its
    ;; parameters. In an open world, however, a function of type $f might come
    ;; in from the outside, so we cannot optimize.
    (local $f (ref null $f))
    (call_ref $f
      (i32.const 0)
      (i64.const 1)
      (local.get $f)
    )
  )
)
