<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/58459>58459</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [coroutines] incorrect transformation removes co_await side effects in non-taken branch
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          jacobsa
      </td>
    </tr>
</table>

<pre>
    Here is a simple program that contains a chain of two coroutines, with one awaiting the other. The awaiter returned by `await_transform` calls an external function (`SomeExternalFunc`) in its `await_suspend` method:

```c++
#include <coroutine>
#include <cstddef>

// A function defined in another translaiton unit.
void SomeExternalFunc();

struct MyTask{
  struct promise_type {
    MyTask get_return_object() { return {std::coroutine_handle<promise_type>::from_promise(*this)}; }
    std::suspend_always initial_suspend() { return {}; }

    void unhandled_exception();
    void return_void() {} 

    auto await_transform(MyTask task) {
      struct Awaiter {
        bool await_ready() { return false; }

        // Resume the lazy coroutine, first calling the external function.
        std::coroutine_handle<promise_type> await_suspend(std::coroutine_handle<promise_type> h) {
          callee.resume_when_done = h;
          SomeExternalFunc();
          return std::coroutine_handle<promise_type>::from_promise(callee);
        }

        // Clean up and then evaluate to null.
        std::nullptr_t await_resume() {
          std::coroutine_handle<promise_type>::from_promise(callee).destroy();
          return nullptr;
        }

        promise_type& caller;
        promise_type& callee;
      };

      return Awaiter{*this, task.handle.promise()};
    }
    
    // Resume the coroutine that started us when we're done.
    auto final_suspend() noexcept {
      struct Awaiter {
        bool await_ready() noexcept { return false; }
        std::coroutine_handle<promise_type> await_suspend(std::coroutine_handle<promise_type> h) noexcept {
          return to_resume;
        }

        void await_resume() noexcept;

        std::coroutine_handle<promise_type> to_resume;
      };

      return Awaiter{resume_when_done};
    }

    // The coroutine to resume when we're done.
    std::coroutine_handle<promise_type> resume_when_done;
  };

  // A handle for the coroutine that returned this task.
  std::coroutine_handle<promise_type> handle;
};

MyTask DoSomethingElse() {
  co_return;
}

// A coroutines that awaits a call to another.
MyTask DoSomething() {
  co_await DoSomethingElse();
  co_return;
}
```

When [compiled](https://godbolt.org/z/e9Mrz9hEa) with `-std=c++20 -O1`, this correctly generates a call to `SomeExternalFunc` from `DoSomething.resume`:

```asm
DoSomething() [clone .resume]:                # @DoSomething() [clone .resume]
[...]
        call    SomeExternalFunc()@PLT
[...]
```

However if we [change]() the `co_await DoSomethingElse();` statement to be a branch:

```c++
if (co_await DoSomethingElse() != nullptr) {}
```

then all mentions of `SomeExternalFunc` are gone from the [generated code](https://gist.github.com/jacobsa/c6f5515f4ed66c93aae56627279b91a6).

**I believe clang is wrong to eliminate this branch.** Although it's true that the branch can never be taken, evaluating the `co_await` expression may have a side effect through the call to `SomeExternalFunc`. (Indeed I found this bug because clang incorrectly removed such side effects in a real codebase.)
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzFWMlu4zgQ_Rr5QkSwJMuODj4kcYIOMI0ZzAToo0FJlKVuijRIKm73108VRS225SyYBiZQvHCp5dWrYtGpzI_rL0wxUmlCia7qPWdkr-RO0ZqYkhqSSWFoJXA6K-EDkQUxBwnjSjamEkx74QM5VKYkUjBCD7SC0R1sZkTCi_LJS-nGmSKKmUYJlpP0SLzl3A5vjaJCF1LVMEIyyjloE4T9hA2CclI0IjOVFMQLb2HFP7Jmj27uCaZgyAsTAqZVRg9CdaP3TOQosmamlLkX3XnzjTfvXmGbfTIvvMenHQ2jSmS8yRnxoofeSS96nJzXJs9ZMcy6NU_wkLvBcFhTodNgIxUWFWJ95mAoTDeiMn6791VWObnwEPwOEy-6H2vRRjWZIV-PL1T_8FZujhA3DkGsK8225rgHU4dp4naQHTPbNhpbmX5nmWm14FoXJfwIDiJu0V0PxbakIueAyMNYBUJg1xUwuHUzVuKdKSuN5q824AHBt96UXroL1pbyAz1qwAlIRHkfwynLzuUNUi2GjWjtzLfsZ8b2GIYzGPulDgX8PGgCueRcMG2MJOeUDW8doAbj4Db3W_p43LkEOJslJJWSO6GK0fx46WxBOWA57Sv-Ob79zXRTM5t4nP46DhmKCVpUShubW11yXqSXfyr1E5EnpxkX3n5mbzmBGf6hrYz5ynq1PZRMbHOsMF60gT3Rxfp3kmZY6FD9r8xu7ZtS8XaYHjiD4tbsoRLkGAgodK-UN9RA6CQRDefXAoFze6O2pqcLQjOi7Jmjv81DP2fAYnl8D1Jn4EcBObEhXLYRv9w9uYydLWurwaUWZ5nLPgSpK0gPNmH9FhF_XLGSsbAT--2X4dNF3vVAt2enNlQZKPuNJshfcgD5Kzhskcf-WVmBA-Ki4AnZ1q7fVFLG4q7Xlv8l_695OgqhkR3hP0gvW9snMqXTNcmXz_l9zaYPs_G8ul1l3gXrXk7pJkkr6k2ifca1C8sGs6a863ueViCBo3EqI_r2D3OwTcChb_kEZ9xEZ8SFQe5I3kg8FUCX2D1yPVEqM-l6oBNZU73c0O-2nlhi2a4YyhHi7xo7_5oBk8qtlGkzR4C_ZWXXxY6N_oYc8OL7TNb7ClogL96AzNKYvUaIrUs7maeSG18qsOzpF_yz5Kv6lZSPFM20DT2IvbFh2bgeOZyTmz8D23A_tCEEWBT0jvwI_aRgCk6xMSbT3TrBcwbnRn67cx5FX2nTqa7bkQlMwVWOvUEnBfyNRhWkO38j4i3mH9ve6o7vfd_vv3aCrHfkasexmP_1x8ukgMlgfZEH9gpVvCogba0twO4da2OG5mEa4TXlXbYAsnDiGFYzYRD9FK5dJIVGNSs_dPcBC_DUf1MPrAiwA-uO-r5ZfsND2-QgZmgYNJoa75BXqEGhau0wFpYj1vX4vqNWDnTL2SSbK238HXC2SX1gPYx8p5lMNXD5KVsWcRzExYLly2WWRJSyeLkMV-EqSZOALrHBOU15fJ4BPl5BZEjGIR54QT4oib2zJDBRw3GNHRvmQAux3-4jdxwum82uhNsoFGGoFqpxxQ-9adcChaBbsmGHIBn6gwnMKNcIdh36KOgIDfu5B35qvE_W9Ag18JXZOztcRVlRQBLCJmU128L7Zgr6GOlnkTPA9BmKdSNcSU6bHZiU0Ub3joshxxWr5Sts0Q34MNKs7c0WpuE6gSFKqYZzJ0xmbB0sl8sgXq3m8SxfR3kSJXRmKsPZ2hao_keEeDNoIv3titrrc6tWDwXzXLWQ4sai6PCdNYqvzxkyIgfnr93bDZwt7e33qdK6wZ8znuLbRZzMynUSMrpYBIs4KEJgzSqK04gmQZCHSRQCSDNOgSQaPQH7Z9U6nIdhMA-SeRzF89gvgpgGNF7li2QRR2kKpYHVtOI-KsbKO1NrawOgrmGSA4v1MEkh2DvBWCcf-sRSqrVj9syau7a2_guihHCO">