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

    <tr>
        <th>Summary</th>
        <td>
            [bug] clang miscompiles coroutine, resuming it from final suspend
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

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

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

<pre>
    I believe I've found a bug in clang's implementation of symmetric coroutine control transfer ([P0913R0](https://wg21.link/P0913R0)). Under some conditions it seems like **clang will resume a coroutine after it hits its final suspend point, without anything in the program saying it should do so**.

---

Below is a program with a promise type that uses straightforward symmetric control transfer:

* The promise arranges for all coroutines to start out suspended at the initial suspend point.

* The promise supports `co_await`ing calls to other coroutines. It does this by suspending itself, storing its handle in the child to resume when the child is done, and then resuming the child. Later the child resumes the parent from its final suspend point.

* The promise also supports `co_await`ing a special marker type (`ResumeFromAwaitSuspend`) that causes `await_ready` to return false but then `await_suspend` to return the input handle, saying "I decided to resume after all".

* The promise doesn't support being resumed from its final suspend point. If this happens, it writes an error to stderr and then exits.

The program calls a coroutine (`Outer`) that `co_await`s a `ResumeFromAwaitSuspend` marker, and then calls and `co_await`s an inner coroutine that also awaits a `ResumeFromAwaitSuspend`. This seems to cause clang to miscompile, because `Why did we resume from final_suspend??` is printed and the program exits with code `1`.

```c++
#include <unistd.h>

#include <coroutine>
#include <iostream>

class Promise;

// An object that can be co_awaited, but we always resume immediately from
// await_suspend.
struct ResumeFromAwaitSuspend{};

struct Task {
  using promise_type = Promise;
  Promise& promise;
};

struct Promise {
  static std::coroutine_handle<Promise> GetHandle(Promise& promise) {
    return std::coroutine_handle<Promise>::from_promise(promise);
  }

  void unhandled_exception() {}
  Task get_return_object() { return Task{*this}; }
  void return_void() {}

  // Always suspend before starting the coroutine body. We actually run the body
  // when we are co_awaited.
  std::suspend_always initial_suspend() { return {}; }

  // We support awaiting tasks. We do so by configuring them to resume us when
  // they are finished, and then resuming them from their initial suspend.
  auto await_transform(Task&& task) {
    struct Awaiter {
      bool await_ready() { return false; }

      std::coroutine_handle<> await_suspend(
          const std::coroutine_handle<> handle) {
        // Tell the child to resume the parent once it finishes.
        child.resume_at_final_suspend = GetHandle(parent);

        // Run the child.
        return GetHandle(child);
      }

      void await_resume() {
        // The child is now at its final suspend point, and can be destroyed.
        return GetHandle(child).destroy();
      }

      Promise& parent;
      Promise& child;
    };

    return Awaiter{
        .parent = *this,
        .child = task.promise,
    };
  }

  // Make evaluation of `co_await ResumeFromAwaitSuspend{}` go through the
  // await_suspend path, but cause it to resume immediately by returning our own
  // handle to resume.
  auto await_transform(ResumeFromAwaitSuspend) {
    struct Awaiter {
      bool await_ready() { return false; }

      std::coroutine_handle<> await_suspend(const std::coroutine_handle<> h) {
        return h;
      }

      void await_resume() {}
    };

    return Awaiter{};
  }

  // Always suspend at the final suspend point, transferring control back to our
  // caller. We expect never to be resumed from the final suspend.
  auto final_suspend() noexcept {
    struct FinalSuspendAwaitable final {
      bool await_ready() noexcept { return false; }

      std::coroutine_handle<> await_suspend(std::coroutine_handle<>) noexcept {
        return promise.resume_at_final_suspend;
      }

      void await_resume() noexcept {
        std::cerr << "Why did we resume from final_suspend??\n";
        _exit(1);
      }

      Promise& promise;
    };

    return FinalSuspendAwaitable{.promise = *this};
  }

  // The handle we will resume once we hit final suspend.
  std::coroutine_handle<> resume_at_final_suspend;
};

static Task Inner() { co_await ResumeFromAwaitSuspend(); }

static Task Outer() {
  co_await ResumeFromAwaitSuspend();
  co_await Inner();
}

int main(int argc, char** argv) {
  // Call Outer and get a reference to its promise, which is suspended at the
  // initial suspend point.
  Promise& promise = Outer().promise;

  // Tell the promise to terminate the chain of resumptions with a no-op
  // coroutine once it's done.
  promise.resume_at_final_suspend = std::noop_coroutine();

  // Resume it from the initial suspend point.
  const std::coroutine_handle<> promise_handle = Promise::GetHandle(promise);
  promise_handle.resume();

  // The promise's coroutine is now suspended at its final suspend point. Clean
  // up after it.
  promise_handle.destroy();
}
```

---

I've verified this bug happens with clang built from `HEAD`, at `-01` but not `-O0`:

```shell
> ./bin/clang++ --version
clang version 15.0.0 (https://github.com/llvm/llvm-project.git 91ab4d4231e5b7456d012776c5eeb69fa61ab994)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/google/home/jacobsa/clients/llvm-project/build/./bin

> ./bin/clang++ -std=c++20 -O0 ~/tmp/foo.cc
> ./a.out
> ./bin/clang++ -std=c++20 -O1 ~/tmp/foo.cc
> ./a.out 
Why did we resume from final_suspend??
```

This also reproduces in [Compiler Explorer](https://godbolt.org/z/Wjnb8Yh6E) with x86-64 clang 14.0.0.
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzNWdtu4zgS_RrnhbBgy5ckD3lInPZOgF3MoDeLxj4ZlERb7MiiIVJxvF-_p0hKpuRL0jNYYAeetG2RVcVTpy4sJyo7PLywRBRSvAv2Mohv8c9a1WXGOEvqDZMlSwtebvBEM7ndFWIrSsONVCVTa6YP260wlUxZqipVG1kKvCtNpQpmKl7qtajYIL4bzJ7-GN2PJ99Hg9kzPufG7PRg8jiIl3jtN_E4KmT5hvfNsvger4j9q8wgQautlZtJUgxDDNNCbDUr5JuAfMh5tGayvSwKVgldYwMPjOJrAznYl0tD-zVby5IXTNd6J3DanZKlGcQLCDA59jBeHkwuS4uAyQXbVWpT8S3T_GC_hQVYV2QsUzDPmRANRs-D0aP7OxwOw49PolB7JjWsakSRKvdxK7Vg5rDDn5wbVmuhmQZ-cpObtar2vMo6UHcBJhwDTTCEvTqLrVxeYd0GEiGJccDToqKZgfGGV4bRkT0WAr439syyBN59kKJrunS926kK6A7mo1St-J5Lg7eEWArVVqGC6CowImIvBiiSNTnwSQ6NPoezFsWaHKONqvw3LOdlVojGNWku4QdI9n7f5yJ8AJmZKgXJwDZ6ULqVJK1dFrG_c6LIcZ-Tpp33eQXaszWOeYk8V3HhhVbXwOEMklLCesurNzKDyECBMx99t3YsIemRdvzTKcUDBIjjS8otY_CVlbmqBM8O-OQwMXVVsjUsEIho487fLtWttGCx8_0Oix3QFn7H-0Ecv7AMphJNjpC78IKH8fwqEOTnEsnENGgg-ZBcJye7jjB7WTuO5HyHrzXZhUDcV9Lg-LxkoqrAcUtqpI3q6G_xAZkdw16DmHbUDNOFQ_73GqcKge46jrZc84_3ZYd4XhU-nsgqgXkZRobTaaljl32mLwLSAMdlRoBgaeHSN30E_qna7qTzZyLcY-z7kR9YJjO2F407rResB1qCTJb0wqGgYYdANJQm3KlaHC3KLq2lKrOyx2RXhxCA077SQfxEL0-TiSzToqZNk0VdSjgwygeTb10uhYtamI6rOs-lQgYVfNsTAji0Zn84Og4mT10FVI3YI0pb8lOkpomuEmixxlkis_AhOPYU13t-0A1sEhk6k0gjxcFC2JHaiTcPCSysoeaCR2-fBrfPPRP9jleu3xgtsF8yVAwKIh9kK5c8Js8nx2TtN_G8WX1UcEmZ3xPq09QCpBRmVHwmj60zVj5jTBat7m_sb8L85hPJ3RkDEF2BaNZkoa8Jd0sI7VUr7-4oOTg6nS84HGPvCqSvSyc0W4mPVOyov6Dg9zY1W5hDfCMouZJ1K8eQ49LGalpHO-NHylQOUxbIsUq9DHp_RllrsKej41iTChOBOi5c2W4rWJsyEnR0EfsBZqamRq45sKp2CZ2e9CTbQkksrkJ6R0cnewd41SvPdt8WtKnhBIOWuaeYe80_2lbBxYU9CJDT1nbbUlEjgE5nLTd15c-5DSpOra35PblYdLDHQfKSOnexerbqb12SwztZ9RudFgFeG597V67ZUtUWx7U-jufEYWPf9gjs48YGM_W_4TMGR6B3Cwv1CX62WJ-Fz0m_HBcUbd3CDuHBXvoPoGrzqZSm8t_3zW-xfhXoJc_1X0HDpMpUUIn27tBRV5LrvNy2FTerTsmxGSxMHU5mGNRn7fpeB91fT6EHOJRql3UzhRV2DnobvI3ryOgges9CFDahJfp_lJMrdw8iqq82mQCJ1EH8wgEiv8fZ9JXjhLnYYdvdFDx3SsLHp-UisM9T_wSXyPOCXNukyHjRW-MQoyUUXVGbzBfnlZ85nIf_Hxy3Q_HOi7q9sAZd1_Wyi05no8AjBMcmJz71ZHeiDPCZvGkLXF8F-ceICBsDpDUHEiUiVVdM7ftZzF9v2v2f5aMLB_n_TktfTENnw8sblP_VmD2W5a_z-UvM69Vsf5--EPbNPd6WueZyn_D0zd6W66onm64QorKFUnzsqE0txbuw155EdC9SJ0q7TOp2-A6WUrk-6Cx1lrTB08tCwpOi0fA1OoXi_ze8-mT95TMG7vYp51Jl-iu0u6z7aDhdXa21C7pv_8L1bLZA8xr3zGNsRRcz6B__ibpwcoP4JFLOUgRnbdJ4J_V_KZaoiPqMCAjCAZ_tLvBd7jqMMzT_jDyfOfjcncjefOx14IXu60Ge_KyyNIW5f9RQpps5nLQVXxV9sj6wsXOqQD3SENtySfceesurTUppKc155eaa9NV7zyDvnAUNFK3NtnvBBYlxoIpsJsg5SDLU7xxLOJp2mebUDvWnjT3B12aP5xhqeRWAF51cb3usarrXdvyKai8qXA9Qpn3_CEyoZ7Ak2bnJs5_Zlmqodv283F7CfNdrZ-Y0emzN_iSv2DO0lC2V2q2OU46eC7u6v_s-wxzz_nUAv1Z9m4GCD7_OSIG2dbrzc3furoAozIQXHXP0iQXwCKvvoTvEuTgqXBSC9_uqetf-DtB3SWPh-R76GDDN9Co0vTfp97-ioCTLtaQpqZ1q15tmaukHZHYsl9Sy8D6D0N--PT7bceOCuWHj0M7PbFdZKvfN71Z7b-LfWIUrVlH47-C-CIdOKK6X_jccGrix4RCmaZpzNAMxGOK_YuNZNIpG7OQnmg1srpMoVeg3l0Xx3vwzBH40CImwgN2PeTLNpvFkLGbJ7XQ2z0bj-PZ2ns6ESOb3az7Hgvv7KUFrdb8isQjcOh7Zx918NZ8O6_INLi6HhSzrj-GmrP26nHoItlWZKGj1Tmn54dEGj6khyp4l_RBCrq51RdYpNEpkuVIbIugyV0S95U-eqkRzC4rEXUT3TkKY1fZGtWzwC6G-DKsNpmc_1oxHDK5CwkTDsTTbHf6ulYrStCuGR-D2n5A8_ppk5r77hQ7iMsvtbNkOoysBqLI6FTQKYoPZ08KNlSv27WNXqAop-MyPfBuVJaowkapwruV_8P-Pn2Vy9-98bnsyGxRgwXA-9bExnhIVo5vsYZLdT-75jZGmEA_Qh2CCBr_sONYOcgWFUDvtabJiJ0_c1FXx8Gscx0epdS2IMbP5JL6_yR9maZby8d0kno1u78R0JuZcjO_GGZ-NOZ-N4puCJ6LQZDU6s1LQT4AQQV3a7PlGPsSjOB7djsaj-XQ2GkXxdHY745N0so75ZD7NBtORQGkuIrKDoLupHqxJgEDjYSG10ceHXGu5KYUFieSjy89V9eApf2NVP1jT_ws7A1iS">