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

(module
 ;; The signature should be refined to a single self-referential type.

 ;; CHECK:      (type $refined (func (param (ref (exact $refined))) (result (ref (exact $refined)))))
 (type $refined (func (param (ref (exact $refined))) (result (ref (exact $refined)))))

 ;; CHECK:      (elem declare func $foo)

 ;; CHECK:      (func $foo (type $refined) (param $0 (ref (exact $refined))) (result (ref (exact $refined)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $foo
 ;; CHECK-NEXT:    (ref.func $foo)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $foo)
 ;; CHECK-NEXT: )
 (func $foo (param funcref) (result funcref)
  (drop
   (call $foo
    (ref.func $foo)
   )
  )
  (ref.func $foo)
 )
)

(module
 ;; The signatures should be refined to a pair of mutually self-referential types.

 ;; CHECK:      (rec
 ;; CHECK-NEXT:  (type $1 (func (param f32 (ref (exact $1))) (result (ref (exact $0)))))

 ;; CHECK:       (type $0 (func (param i32 (ref (exact $0))) (result (ref (exact $1)))))
 (type $0 (func (param i32 funcref) (result funcref)))
 (type $1 (func (param f32 funcref) (result funcref)))


 ;; CHECK:      (elem declare func $bar $foo)

 ;; CHECK:      (func $foo (type $0) (param $0 i32) (param $1 (ref (exact $0))) (result (ref (exact $1)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $foo
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:    (ref.func $foo)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $bar)
 ;; CHECK-NEXT: )
 (func $foo (type $0) (param i32 funcref) (result funcref)
  (drop
   (call $foo
    (i32.const 0)
    (ref.func $foo)
   )
  )
  (ref.func $bar)
 )

 ;; CHECK:      (func $bar (type $1) (param $0 f32) (param $1 (ref (exact $1))) (result (ref (exact $0)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $bar
 ;; CHECK-NEXT:    (f32.const 0)
 ;; CHECK-NEXT:    (ref.func $bar)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $foo)
 ;; CHECK-NEXT: )
 (func $bar (type $1) (param f32 funcref) (result funcref)
  (drop
   (call $bar
    (f32.const 0)
    (ref.func $bar)
   )
  )
  (ref.func $foo)
 )
)

(module
 ;; The signatures start out as separate types, so they must remain separate
 ;; even though they are refined to the same structure.

 (rec
  ;; CHECK:      (rec
  ;; CHECK-NEXT:  (type $1 (func (param (ref (exact $0)))))

  ;; CHECK:       (type $0 (func (param (ref (exact $0)))))
  (type $0 (func (param funcref)))
  (type $1 (func (param funcref)))
 )

 ;; CHECK:      (elem declare func $foo)

 ;; CHECK:      (func $foo (type $0) (param $0 (ref (exact $0)))
 ;; CHECK-NEXT:  (call $foo
 ;; CHECK-NEXT:   (ref.func $foo)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $foo (type $0) (param funcref)
  (call $foo
   (ref.func $foo)
  )
 )

 ;; CHECK:      (func $bar (type $1) (param $0 (ref (exact $0)))
 ;; CHECK-NEXT:  (call $bar
 ;; CHECK-NEXT:   (ref.func $foo)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 (func $bar (type $1) (param funcref)
  (call $bar
   (ref.func $foo)
  )
 )
)

(module
 ;; The signatures should be refined to a pair of mutually recursive types and
 ;; another type that refers to them.

 ;; CHECK:      (rec
 ;; CHECK-NEXT:  (type $2 (func (param (ref (exact $0))) (result (ref (exact $1)))))

 ;; CHECK:       (type $1 (func (param f32 (ref (exact $1))) (result (ref (exact $0)))))

 ;; CHECK:       (type $0 (func (param i32 (ref (exact $0))) (result (ref (exact $1)))))
 (type $0 (func (param i32 funcref) (result funcref)))

 (type $1 (func (param f32 funcref) (result funcref)))

 (type $2 (func (param funcref) (result funcref)))


 ;; CHECK:      (elem declare func $bar $foo)

 ;; CHECK:      (func $foo (type $0) (param $0 i32) (param $1 (ref (exact $0))) (result (ref (exact $1)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $foo
 ;; CHECK-NEXT:    (i32.const 0)
 ;; CHECK-NEXT:    (ref.func $foo)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $bar)
 ;; CHECK-NEXT: )
 (func $foo (param i32 funcref) (result funcref)
  (drop
   (call $foo
    (i32.const 0)
    (ref.func $foo)
   )
  )
  (ref.func $bar)
 )

 ;; CHECK:      (func $baz (type $2) (param $0 (ref (exact $0))) (result (ref (exact $1)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $quux
 ;; CHECK-NEXT:    (ref.func $foo)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $bar)
 ;; CHECK-NEXT: )
 (func $baz (param funcref) (result funcref)
  (drop
   (call $quux
    (ref.func $foo)
   )
  )
  (ref.func $bar)
 )

 ;; CHECK:      (func $bar (type $1) (param $0 f32) (param $1 (ref (exact $1))) (result (ref (exact $0)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $bar
 ;; CHECK-NEXT:    (f32.const 0)
 ;; CHECK-NEXT:    (ref.func $bar)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $foo)
 ;; CHECK-NEXT: )
 (func $bar (param f32 funcref) (result funcref)
  (drop
   (call $bar
    (f32.const 0)
    (ref.func $bar)
   )
  )
  (ref.func $foo)
 )

 ;; CHECK:      (func $quux (type $2) (param $0 (ref (exact $0))) (result (ref (exact $1)))
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (call $baz
 ;; CHECK-NEXT:    (ref.func $foo)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (ref.func $bar)
 ;; CHECK-NEXT: )
 (func $quux (param funcref) (result funcref)
  (drop
   (call $baz
    (ref.func $foo)
   )
  )
  (ref.func $bar)
 )
)
