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

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

  ;; CHECK:      (table $t 0 funcref)
  (table $t 0 funcref)

  ;; CHECK:      (tag $e (type $1) (param i32))
  (tag $e (param i32))
  ;; CHECK:      (tag $e2 (type $1) (param i32))
  (tag $e2 (param i32))

  ;; CHECK:      (func $try-test (type $void)
  ;; CHECK-NEXT:  (nop)
  ;; CHECK-NEXT: )
  (func $try-test
    ;; When try body does not throw, try-body can be replaced with the try body
    (try
      (do
        (drop (i32.const 0))
      )
      (catch $e
        (drop (pop i32))
      )
    )
  )

  ;; CHECK:      (func $inner-try-catch_all-test (type $2) (result i32)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (try
  ;; CHECK-NEXT:   (do
  ;; CHECK-NEXT:    (throw $e
  ;; CHECK-NEXT:     (i32.const 0)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (catch_all
  ;; CHECK-NEXT:    (return
  ;; CHECK-NEXT:     (i32.const 1)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $inner-try-catch_all-test (result i32)
    (local $0 i32)
    ;; The exception thrown in the inner try is caught by the inner catch_all,
    ;; so the outer try body does not throw and the outer try-catch can be
    ;; removed
    (try
      (do
        (try
          (do
            (throw $e (i32.const 0))
          )
          (catch_all
            (return (i32.const 1))
          )
        )
      )
      (catch $e
        (drop (pop i32))
      )
    )
    (i32.const 2)
  )

  ;; CHECK:      (func $inner-try-catch-test (type $void)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (try
  ;; CHECK-NEXT:   (do
  ;; CHECK-NEXT:    (try
  ;; CHECK-NEXT:     (do
  ;; CHECK-NEXT:      (throw $e2
  ;; CHECK-NEXT:       (i32.const 0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (catch $e
  ;; CHECK-NEXT:      (drop
  ;; CHECK-NEXT:       (pop i32)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:      (local.set $0
  ;; CHECK-NEXT:       (i32.const 1)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (catch $e
  ;; CHECK-NEXT:    (drop
  ;; CHECK-NEXT:     (pop i32)
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $inner-try-catch-test (local $0 i32)
    ;; The exception thrown in the inner try will not be caught by the inner
    ;; catch, so the outer try-catch cannot be removed
    (try
      (do
        (try
          (do
            (throw $e2 (i32.const 0))
          )
          (catch $e
            (drop (pop i32))
            (local.set $0 (i32.const 1))
          )
        )
      )
      (catch $e
        (drop (pop i32))
      )
    )
  )

  ;; CHECK:      (func $br-in-catch (type $void)
  ;; CHECK-NEXT:  (unreachable)
  ;; CHECK-NEXT: )
  (func $br-in-catch
    ;; When catch body is removed, the removal of 'br' inside the catch body
    ;; should be propagated up to the outer block, so that its type will be
    ;; correctly updated to unreachable.
    (block $label$1
      (try
        (do
          (unreachable)
        )
        (catch $e
          (drop (pop i32))
          (br $label$1)
        )
      )
    )
  )

  ;; CHECK:      (func $try-delegate-outer-target (type $void)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (try $label$0
  ;; CHECK-NEXT:   (do
  ;; CHECK-NEXT:    (try
  ;; CHECK-NEXT:     (do
  ;; CHECK-NEXT:      (try
  ;; CHECK-NEXT:       (do
  ;; CHECK-NEXT:        (throw $e
  ;; CHECK-NEXT:         (i32.const 0)
  ;; CHECK-NEXT:        )
  ;; CHECK-NEXT:       )
  ;; CHECK-NEXT:       (delegate $label$0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (catch_all
  ;; CHECK-NEXT:      (nop)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $try-delegate-outer-target
    (local $0 i32)
    (try $label$0  ;; outer try
      (do
        ;; If it were not for the inner (delegate $label0), this middle try
        ;; cannot throw even if there is a throw in the inner try, because this
        ;; try has a catch_all. And Vacuum can replace the outer try-catch with
        ;; the try's body if the body doesn't throw.
        ;;
        ;; But because the inner try has a delegate that targets the outer try,
        ;; this middle try can throw, and we can't do the optimization for
        ;; the outer try.
        (try  ;; middle try
          (do
            (try  ;; inner try
              (do
                (throw $e
                  (i32.const 0)
                )
              )
              (delegate $label$0)
            )
          )
          (catch_all)
        )
      )
    )
  )

  ;; CHECK:      (func $trivial-catch-all-of-throw (type $void)
  ;; CHECK-NEXT:  (local $0 i32)
  ;; CHECK-NEXT:  (try
  ;; CHECK-NEXT:   (do
  ;; CHECK-NEXT:    (if
  ;; CHECK-NEXT:     (local.get $0)
  ;; CHECK-NEXT:     (then
  ;; CHECK-NEXT:      (throw $e
  ;; CHECK-NEXT:       (i32.const 0)
  ;; CHECK-NEXT:      )
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:     (else
  ;; CHECK-NEXT:      (unreachable)
  ;; CHECK-NEXT:     )
  ;; CHECK-NEXT:    )
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:   (catch_all
  ;; CHECK-NEXT:    (nop)
  ;; CHECK-NEXT:   )
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $trivial-catch-all-of-throw (local $0 i32)
    ;; This try-catch's body throws, but the catch-all catches it, so the entire
    ;; try can be optimized out.
    (try
      (do
        (throw $e (i32.const 0))
      )
      (catch_all)
    )
    ;; Here we also have a possible trap, so we can't do it.
    (try
      (do
        (if
          (local.get $0)
          (then
            (throw $e (i32.const 0))
          )
          (else
            (unreachable)
          )
        )
      )
      (catch_all)
    )
  )

  ;; CHECK:      (func $throw (type $void)
  ;; CHECK-NEXT:  (throw $e
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $throw
    ;; Helper for the tail call tests below.
    (throw $e
      (i32.const 0)
    )
  )

  ;; CHECK:      (func $return-call-catch (type $void)
  ;; CHECK-NEXT:  (return_call $throw)
  ;; CHECK-NEXT: )
  (func $return-call-catch
    (try
      (do
        ;; This returns before it throws, so we can optimize out the surrounding
        ;; try-catch.
        (return_call $throw)
      )
      (catch_all)
    )
  )

  ;; CHECK:      (func $return-call-indirect-catch (type $void)
  ;; CHECK-NEXT:  (return_call_indirect $t (type $void)
  ;; CHECK-NEXT:   (i32.const 0)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $return-call-indirect-catch
    (try
      (do
        ;; This returns before it throws, so we can optimize out the surrounding
        ;; try-catch.
        (return_call_indirect
          (i32.const 0)
        )
      )
      (catch_all)
    )
  )

  ;; CHECK:      (func $return-call-ref-catch (type $void)
  ;; CHECK-NEXT:  (return_call_ref $void
  ;; CHECK-NEXT:   (ref.func $throw)
  ;; CHECK-NEXT:  )
  ;; CHECK-NEXT: )
  (func $return-call-ref-catch
    (try
      (do
        ;; This returns before it throws, so we can optimize out the surrounding
        ;; try-catch.
        (return_call_ref $void
          (ref.func $throw)
        )
      )
      (catch_all)
    )
  )
)
