Executable version with step-by-step log: https://godbolt.org/z/z83bofdev
Minimum reproduction:
#include <exception>
bool g_coro_value_destroyed = false;
struct CoroValue {
struct promise_type {
auto get_return_object() -> CoroValue {
return CoroValue{};
}
auto initial_suspend() -> std::suspend_never { return {}; }
auto final_suspend() noexcept -> std::suspend_always { return {}; }
void return_void() {}
void unhandled_exception() {
std::rethrow_exception(std::current_exception());
}
};
~CoroValue() {
g_coro_value_destroyed = true;
}
};
auto
failing_coro() -> CoroValue {
throw 0;
co_return;
}
auto
main() -> int {
try {
CoroValue value = failing_coro();
} catch (...) {}
if (g_coro_value_destroyed) {
return 0;
}
return 1;
}
Observed behavior: Process exit status code is 1, indicating that the destructor of CoroValue was never called.
Expected behavior: Process exit status code is 0 after the destructor of CoroValue was called.
Investigation:
The standard (at least cppreference) states in the execution section of the coroutines page defines this step:
...
calls promise.get_return_object() and keeps the result in a local variable. The result of that call will be returned to the caller when the coroutine first suspends.
...
The observed behavior is that this local variable is not destroyed properly, as if the lifetime was immediately bound to the caller-side return value variable. However, if the flow never reaches this caller-side variable, then it is not destroyed during the stack unwinding triggered by the exception.
clang version:
$ clang --version
Ubuntu clang version 22.1.6 (++20260516103140+fc4aad7b5db3-1~exp1~20260516103204.74)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/lib/llvm-22/bin
Related issue: #61900
Executable version with step-by-step log: https://godbolt.org/z/z83bofdev
Minimum reproduction:
Observed behavior: Process exit status code is 1, indicating that the destructor of
CoroValuewas never called.Expected behavior: Process exit status code is 0 after the destructor of
CoroValuewas called.Investigation:
The standard (at least cppreference) states in the execution section of the coroutines page defines this step:
The observed behavior is that this local variable is not destroyed properly, as if the lifetime was immediately bound to the caller-side return value variable. However, if the flow never reaches this caller-side variable, then it is not destroyed during the stack unwinding triggered by the exception.
clang version:
Related issue: #61900