Must coroutine return type be copy- or move-constructible if it is passed in co_await

Viewed 128

In the following code example, the lambda coroutine g performs co_await f, where f is coroutine return type with deleted copy- and move- constructors:

#include <coroutine>

class task {
public:
    class task_promise {
    public:
        task get_return_object() { return task{handle_type::from_promise(*this)}; }
        auto initial_suspend() { return std::suspend_always{}; }
        auto final_suspend() noexcept { return std::suspend_never{}; }
        void return_void() {}
        void unhandled_exception() {}
    };
    using handle_type = std::coroutine_handle<task_promise>;
    handle_type handle;

    auto await_ready() { return false; }
    auto await_suspend(handle_type) { return handle; }
    void await_resume() { handle.resume(); }

    using promise_type = task_promise;
    task(handle_type h) : handle(h) {}
    task(const task&) = delete;
    task& operator=(const task&) = delete;
    task(task&&) = delete;
    task& operator=(task&&) = delete;
};

int main() {
    task f = []() -> task { co_return; }();
    task g = [&f]() -> task { co_await f; }();
    g.handle.resume();
}

The code is accepted in MSVC, but not in GCC.

GCC 11.2 complains on the missing move-constructor:

error: use of deleted function 'task::task(task&&)'

and GCC trunk - on the missing copy-constructor:

error: use of deleted function 'task::task(const task&)'

Demo: https://gcc.godbolt.org/z/1c7fajqn8

Which compiler is right here?

P.S. There is a related question Why must the return type of a coroutine be move-constructible? but

  • the issue there was fixed in GCC 10.2, while the example from this question is still valid in GCC 11 and trunk;
  • this question asks about co_await requirements, while the other does not mention co_await at all;
  • the other question is about move-constructor only, while here the latest GCC demands copy-constructor.
1 Answers

It is a compiler bug. Specifically,

If o would be a prvalue, the temporary materialization conversion (7.3.4) is applied -- co_await 7.6.2.3

Which basically means it should be constructed in place using RVO.

Related