https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95599
Bug ID: 95599
Summary: [coroutines] destructor for temporary operand to
co_yield expression called before end of
full-expression
Product: gcc
Version: 10.1.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
Assignee: unassigned at gcc dot gnu.org
Reporter: lewissbaker.opensource at gmail dot com
Target Milestone: ---
If I write the statement:
co_yield T{};
Then I expect the T constructor to be called before the coroutine suspends
and that the T destructor will not be called until after the coroutine
resumes and execution reaches the semicolon (ie. end of full expression).
However, I am observing that the T destructor is being called before the
await_suspend() method on the awaitable returned from promise.yield_value()
is called (ie. before the coroutine suspends).
To reproduce, compile the following code sample using GCC trunk.
Flags: -std=c++2a -fcoroutines
-------
#include <coroutine>
using namespace std;
#include <cstdio>
#include <utility>
struct resource {
resource() { std::printf("resource()\n"); }
~resource() { std::printf("~resource()\n"); }
resource(resource&&) = delete;
};
template<typename T>
struct generator {
struct promise_type {
generator get_return_object() {
return
generator{coroutine_handle<promise_type>::from_promise(*this)};
}
void return_void() {}
void unhandled_exception() {}
suspend_always initial_suspend() { return {}; }
suspend_always final_suspend() { return {}; }
struct awaitable {
resource& r;
awaitable(resource&& r) : r(r) {}
~awaitable() {}
bool await_ready() noexcept { return false; }
void await_suspend(coroutine_handle<> h) noexcept {
std::printf("awaitable::await_suspend()\n");
}
void await_resume() noexcept {
std::printf("awaitable::await_resume()\n");
}
};
awaitable yield_value(resource&& r) {
return awaitable{std::move(r)};
}
};
generator(coroutine_handle<promise_type> coro) : coro(coro)
{}
generator(generator&& g) noexcept : coro(std::exchange(g.coro, {}))
{}
~generator() {
if (coro) { coro.destroy(); }
}
coroutine_handle<promise_type> coro;
};
generator<int> f() {
co_yield resource{};
}
int main() {
generator x = f();
x.coro.resume();
x.coro.resume();
}
-------
The expected output of this program is:
```
resource()
awaitable::await_suspend()
awaitable::await_resume()
~resource()
```
However, the observed output of this program is:
```
resource()
~resource()
awaitable::await_suspend()
awaitable::await_resume()
```
i.e. the temporary is not being kept alive until the end
of the full-expression containing the co_yield statement.