https://gcc.gnu.org/bugzilla/show_bug.cgi?id=112360
Bug ID: 112360 Summary: [coroutines] unreachable 'co_await' still creates a suspension point Product: gcc Version: 14.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: oremanj at mit dot edu Target Milestone: --- A coroutine function of the form: task example() { do { co_await awaitable{}; } while (false && co_await awaitable{}); co_return; } will execute the awaitable's await_ready() and await_suspend() methods twice, even though execution of the abstract machine only reaches one co_await statement. This reproduces on several tested versions from 11.1 to current master, at either of optimization level -O0 or -O2. While the construct may seem a bit nonsensical, it does show up in practice; it's simplified from something that appears after macro expansion when using the popular Catch2 testing library if you write an assertion about the value of a co_await expression. Compiler Explorer link: https://godbolt.org/z/EoP7EnEnf Full test case is below. On gcc it prints: ready suspend resume ready suspend returned final suspend clang (14 through 17) shows the more expected ready suspend resume returned final suspend command line: gcc -std=c++20 -Wall -Wextra t.c -o t --- test case follows --- #include <coroutine> extern "C" int puts(const char* s); struct task { struct promise_type { std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { puts("final suspend"); return {}; } void return_void() { puts("returned"); } void unhandled_exception() {} task get_return_object() { return task{this}; } }; promise_type* promise; }; struct awaitable { bool await_ready() const noexcept { puts("ready"); return false; } void await_suspend(std::coroutine_handle<> h) { puts("suspend"); h.resume(); } bool await_resume() { puts("resume"); return false; } }; task example() { do { co_await awaitable{}; } while (false && co_await awaitable{}); co_return; } int main() { using handle = std::coroutine_handle<task::promise_type>; task t = example(); handle h = handle::from_promise(*t.promise); h.resume(); }