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.

Reply via email to