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

;; RUN: foreach %s %t wasm-opt --gufa --closed-world -all -S -o - | filecheck %s
;; RUN: foreach %s %t wasm-opt --gufa                -all -S -o - | filecheck %s --check-prefix OPEN_WORLD

(module
 ;; CHECK:      (type $func (func))
 ;; OPEN_WORLD:      (type $func (func))
 (type $func (func))
 ;; CHECK:      (type $cont (cont $func))
 ;; OPEN_WORLD:      (type $cont (cont $func))
 (type $cont (cont $func))

 ;; CHECK:      (type $func-i32 (func (result i32)))
 ;; OPEN_WORLD:      (type $func-i32 (func (result i32)))
 (type $func-i32 (func (result i32)))
 ;; CHECK:      (type $cont-i32 (cont $func-i32))
 ;; OPEN_WORLD:      (type $cont-i32 (cont $func-i32))
 (type $cont-i32 (cont $func-i32))

 ;; CHECK:      (type $4 (func (result i32 (ref $cont))))

 ;; CHECK:      (elem declare func $cont $cont-i32)

 ;; CHECK:      (tag $tag (type $func))
 ;; OPEN_WORLD:      (type $4 (func (result i32 (ref $cont))))

 ;; OPEN_WORLD:      (elem declare func $cont $cont-i32)

 ;; OPEN_WORLD:      (tag $tag (type $func))
 (tag $tag (type $func))

 ;; CHECK:      (tag $tag-i32 (type $func-i32) (result i32))
 ;; OPEN_WORLD:      (tag $tag-i32 (type $func-i32) (result i32))
 (tag $tag-i32 (type $func-i32))

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

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

 ;; CHECK:      (export "resume-i32" (func $resume-i32))

 ;; CHECK:      (func $cont (type $func)
 ;; CHECK-NEXT:  (suspend $tag)
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (export "resume" (func $resume))

 ;; OPEN_WORLD:      (export "resume_throw" (func $resume_throw))

 ;; OPEN_WORLD:      (export "resume-i32" (func $resume-i32))

 ;; OPEN_WORLD:      (func $cont (type $func)
 ;; OPEN_WORLD-NEXT:  (suspend $tag)
 ;; OPEN_WORLD-NEXT: )
 (func $cont
  ;; Helper for below.
  (suspend $tag)
 )

 ;; CHECK:      (func $resume (type $func)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block $block (result (ref $cont))
 ;; CHECK-NEXT:    (resume $cont (on $tag $block)
 ;; CHECK-NEXT:     (cont.new $cont
 ;; CHECK-NEXT:      (ref.func $cont)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (return)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (func $resume (type $func)
 ;; OPEN_WORLD-NEXT:  (drop
 ;; OPEN_WORLD-NEXT:   (block $block (result (ref $cont))
 ;; OPEN_WORLD-NEXT:    (resume $cont (on $tag $block)
 ;; OPEN_WORLD-NEXT:     (cont.new $cont
 ;; OPEN_WORLD-NEXT:      (ref.func $cont)
 ;; OPEN_WORLD-NEXT:     )
 ;; OPEN_WORLD-NEXT:    )
 ;; OPEN_WORLD-NEXT:    (return)
 ;; OPEN_WORLD-NEXT:   )
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT: )
 (func $resume (export "resume")
  ;; A continuation is created, it suspends, and we handle that. There is
  ;; nothing to optimize or change here.
  (drop
   (block $block (result (ref $cont))
    (resume $cont (on $tag $block)
     (cont.new $cont
      (ref.func $cont)
     )
    )
    (return)
   )
  )
 )

 ;; CHECK:      (func $resume_throw (type $func)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block $block (result (ref $cont))
 ;; CHECK-NEXT:    (resume_throw $cont $tag (on $tag $block)
 ;; CHECK-NEXT:     (cont.new $cont
 ;; CHECK-NEXT:      (ref.func $cont)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (return)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (func $resume_throw (type $func)
 ;; OPEN_WORLD-NEXT:  (drop
 ;; OPEN_WORLD-NEXT:   (block $block (result (ref $cont))
 ;; OPEN_WORLD-NEXT:    (resume_throw $cont $tag (on $tag $block)
 ;; OPEN_WORLD-NEXT:     (cont.new $cont
 ;; OPEN_WORLD-NEXT:      (ref.func $cont)
 ;; OPEN_WORLD-NEXT:     )
 ;; OPEN_WORLD-NEXT:    )
 ;; OPEN_WORLD-NEXT:    (return)
 ;; OPEN_WORLD-NEXT:   )
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT: )
 (func $resume_throw (export "resume_throw")
  ;; As above, but with resume_throw.
  (drop
   (block $block (result (ref $cont))
    (resume_throw $cont $tag (on $tag $block)
     (cont.new $cont
      (ref.func $cont)
     )
    )
    (return)
   )
  )
 )

 ;; CHECK:      (func $cont-i32 (type $func-i32) (result i32)
 ;; CHECK-NEXT:  (suspend $tag)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT:  (unreachable)
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (func $cont-i32 (type $func-i32) (result i32)
 ;; OPEN_WORLD-NEXT:  (suspend $tag)
 ;; OPEN_WORLD-NEXT:  (unreachable)
 ;; OPEN_WORLD-NEXT:  (unreachable)
 ;; OPEN_WORLD-NEXT: )
 (func $cont-i32 (result i32)
  ;; Helper for below.
  (suspend $tag)
  (unreachable)
 )

 ;; CHECK:      (func $resume-i32 (type $func)
 ;; CHECK-NEXT:  (tuple.drop 2
 ;; CHECK-NEXT:   (block $block (type $4) (result i32 (ref $cont))
 ;; CHECK-NEXT:    (drop
 ;; CHECK-NEXT:     (resume $cont-i32 (on $tag-i32 $block)
 ;; CHECK-NEXT:      (cont.new $cont-i32
 ;; CHECK-NEXT:       (ref.func $cont-i32)
 ;; CHECK-NEXT:      )
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (return)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (func $resume-i32 (type $func)
 ;; OPEN_WORLD-NEXT:  (tuple.drop 2
 ;; OPEN_WORLD-NEXT:   (block $block (type $4) (result i32 (ref $cont))
 ;; OPEN_WORLD-NEXT:    (drop
 ;; OPEN_WORLD-NEXT:     (resume $cont-i32 (on $tag-i32 $block)
 ;; OPEN_WORLD-NEXT:      (cont.new $cont-i32
 ;; OPEN_WORLD-NEXT:       (ref.func $cont-i32)
 ;; OPEN_WORLD-NEXT:      )
 ;; OPEN_WORLD-NEXT:     )
 ;; OPEN_WORLD-NEXT:    )
 ;; OPEN_WORLD-NEXT:    (return)
 ;; OPEN_WORLD-NEXT:   )
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT: )
 (func $resume-i32 (export "resume-i32")
  ;; As above, but with more values sent than just the continuation.
  (tuple.drop 2
   (block $block (result i32 (ref $cont))
    (resume $cont-i32 (on $tag-i32 $block)
     (cont.new $cont-i32
      (ref.func $cont-i32)
     )
    )
    (return)
   )
  )
 )
)

(module
 ;; CHECK:      (type $func (func (param i32)))

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

 ;; CHECK:      (type $none (func))
 ;; OPEN_WORLD:      (type $func (func (param i32)))

 ;; OPEN_WORLD:      (type $cont (cont $func))

 ;; OPEN_WORLD:      (type $none (func))
 (type $none (func))
 (type $func (func (param i32)))
 (type $cont (cont $func))

 ;; CHECK:      (elem declare func $func)

 ;; CHECK:      (tag $tag (type $none))
 ;; OPEN_WORLD:      (elem declare func $func)

 ;; OPEN_WORLD:      (tag $tag (type $none))
 (tag $tag (type $none))

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

 ;; CHECK:      (func $func (type $func) (param $x i32)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (local.get $x)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (export "run" (func $run))

 ;; OPEN_WORLD:      (func $func (type $func) (param $x i32)
 ;; OPEN_WORLD-NEXT:  (drop
 ;; OPEN_WORLD-NEXT:   (local.get $x)
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT: )
 (func $func (param $x i32)
  ;; This function is only ever referred to by a ref.func that is passed into
  ;; a cont.new, and that is the only source of values for this local. We should
  ;; not modify this to unreachable, which we would do if we didn't realize it
  ;; can be reached. (The only possible value is 42, from the caller below, but
  ;; we do not infer that yet TODO)
  (drop
   (local.get $x)
  )
 )

 ;; CHECK:      (func $run (type $none)
 ;; CHECK-NEXT:  (drop
 ;; CHECK-NEXT:   (block $block (result (ref $cont))
 ;; CHECK-NEXT:    (resume $cont (on $tag $block)
 ;; CHECK-NEXT:     (i32.const 42)
 ;; CHECK-NEXT:     (cont.new $cont
 ;; CHECK-NEXT:      (ref.func $func)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (return)
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (func $run (type $none)
 ;; OPEN_WORLD-NEXT:  (drop
 ;; OPEN_WORLD-NEXT:   (block $block (result (ref $cont))
 ;; OPEN_WORLD-NEXT:    (resume $cont (on $tag $block)
 ;; OPEN_WORLD-NEXT:     (i32.const 42)
 ;; OPEN_WORLD-NEXT:     (cont.new $cont
 ;; OPEN_WORLD-NEXT:      (ref.func $func)
 ;; OPEN_WORLD-NEXT:     )
 ;; OPEN_WORLD-NEXT:    )
 ;; OPEN_WORLD-NEXT:    (return)
 ;; OPEN_WORLD-NEXT:   )
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT: )
 (func $run (export "run")
  (drop
   (block $block (result (ref $cont))
    (resume $cont (on $tag $block)
     (cont.new $cont
      (i32.const 42)
      (ref.func $func)
     )
    )
    (return)
   )
  )
 )
)

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

 ;; CHECK:      (type $cont (cont $func))
 ;; OPEN_WORLD:      (type $cont (cont $func))
 (type $cont (cont $func))

 ;; CHECK:      (type $func-i32 (func (param i32)))
 ;; OPEN_WORLD:      (type $func-i32 (func (param i32)))
 (type $func-i32 (func (param i32)))



 ;; CHECK:      (type $3 (func (result i32)))

 ;; CHECK:      (type $4 (func (result i32 (ref $cont))))

 ;; CHECK:      (elem declare func $cont)

 ;; CHECK:      (tag $tag (type $func))
 ;; OPEN_WORLD:      (type $3 (func (result i32)))

 ;; OPEN_WORLD:      (type $4 (func (result i32 (ref $cont))))

 ;; OPEN_WORLD:      (elem declare func $cont)

 ;; OPEN_WORLD:      (tag $tag (type $func))
 (tag $tag (type $func))

 ;; CHECK:      (tag $tag-i32 (type $func-i32) (param i32))
 ;; OPEN_WORLD:      (tag $tag-i32 (type $func-i32) (param i32))
 (tag $tag-i32 (type $func-i32))

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

 ;; CHECK:      (func $cont (type $func)
 ;; CHECK-NEXT:  (suspend $tag-i32
 ;; CHECK-NEXT:   (i32.const 1337)
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (export "resume" (func $resume))

 ;; OPEN_WORLD:      (func $cont (type $func)
 ;; OPEN_WORLD-NEXT:  (suspend $tag-i32
 ;; OPEN_WORLD-NEXT:   (i32.const 1337)
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT: )
 (func $cont
  ;; Helper for below.
  (suspend $tag-i32
   (i32.const 1337)
  )
 )

 ;; CHECK:      (func $resume (type $3) (result i32)
 ;; CHECK-NEXT:  (tuple.drop 2
 ;; CHECK-NEXT:   (block $block (type $4) (result i32 (ref $cont))
 ;; CHECK-NEXT:    (resume $cont (on $tag-i32 $block)
 ;; CHECK-NEXT:     (cont.new $cont
 ;; CHECK-NEXT:      (ref.func $cont)
 ;; CHECK-NEXT:     )
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:    (return
 ;; CHECK-NEXT:     (i32.const 42)
 ;; CHECK-NEXT:    )
 ;; CHECK-NEXT:   )
 ;; CHECK-NEXT:  )
 ;; CHECK-NEXT:  (i32.const 1337)
 ;; CHECK-NEXT: )
 ;; OPEN_WORLD:      (func $resume (type $3) (result i32)
 ;; OPEN_WORLD-NEXT:  (tuple.drop 2
 ;; OPEN_WORLD-NEXT:   (block $block (type $4) (result i32 (ref $cont))
 ;; OPEN_WORLD-NEXT:    (resume $cont (on $tag-i32 $block)
 ;; OPEN_WORLD-NEXT:     (cont.new $cont
 ;; OPEN_WORLD-NEXT:      (ref.func $cont)
 ;; OPEN_WORLD-NEXT:     )
 ;; OPEN_WORLD-NEXT:    )
 ;; OPEN_WORLD-NEXT:    (return
 ;; OPEN_WORLD-NEXT:     (i32.const 42)
 ;; OPEN_WORLD-NEXT:    )
 ;; OPEN_WORLD-NEXT:   )
 ;; OPEN_WORLD-NEXT:  )
 ;; OPEN_WORLD-NEXT:  (i32.const 1337)
 ;; OPEN_WORLD-NEXT: )
 (func $resume (export "resume") (result i32)
  ;; A continuation is created, it suspends, and we handle that. We can even
  ;; infer the suspended value, 1337.
  (tuple.extract 2 0
   (block $block (result i32 (ref $cont))
    (resume $cont (on $tag-i32 $block)
     (cont.new $cont
      (ref.func $cont)
     )
    )
    (return
     (i32.const 42)
    )
   )
  )
 )
)

