https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105406
Bug ID: 105406 Summary: coroutines: since 11.3 co_await attempts to copy a move-only value when await_transform(T &) exists Product: gcc Version: 11.3.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: dvratil at kde dot org Target Milestone: --- Starting with gcc 11.3, gcc tries to copy an object to await_transform() even if an overload that accepts a reference exists: #include <coroutine> #include <exception> // A move-only awaitable class MoveOnlyAwaitable { public: MoveOnlyAwaitable() = default; MoveOnlyAwaitable(MoveOnlyAwaitable &&) = default; MoveOnlyAwaitable &operator=(MoveOnlyAwaitable &&) = default; MoveOnlyAwaitable(const MoveOnlyAwaitable &) = delete; MoveOnlyAwaitable &operator=(const MoveOnlyAwaitable &) = delete; bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle<>) noexcept {} void await_resume() {} }; struct task { struct promise_type { auto initial_suspend() const { return std::suspend_never{}; } auto final_suspend() const noexcept { return std::suspend_never(); } auto get_return_object() { return task{}; } void return_void() {} void unhandled_exception() {} template<typename T> T &&await_transform(T &&t) { return static_cast<T &&>(t); } }; bool await_ready() const { return false; } void await_suspend(std::coroutine_handle<> awaiter) {} void await_resume() {} }; task myCoroutine() { // GCC: OK // clang: OK { co_await MoveOnlyAwaitable(); } // GCC: OK // clang: OK { auto moveonly = MoveOnlyAwaitable(); co_await std::move(moveonly); } // GCC <= 11.2: OK // GCC 11.3:ERROR: error: use of deleted function 'MoveOnlyAwaitable::MoveOnlyAwaitable(const MoveOnlyAwaitable&) // clang: OK { auto moveonly = MoveOnlyAwaitable(); co_await moveonly; } } Both gcc<11.3 and clang invoke MoveOnlyAwaitable& task::promise_type::await_transform<MoveOnlyAwaitable&>(MoveOnlyAwaitable&) for the last co_await, while GCC 11.3 and on seems to attempt to pass `moveOnly` by value. Manually instantiating the template, or creating a non-template overload that accepts MoveOnlyAwaitable& doesn't work either. I believe that this is a bug because calling auto move_only = MoveOnlyAwaitable(); task::promise_type p; p.await_transform(moveonly); works even with GCC 11.3. As expected, the compiler produces the await_transform<MoveOnlyAwaitable&>(MoveOnlyAwaitable&) overload, so it's weird that it doesn't do so when `move_only` is passed to `co_await`.