Bug 105406 - [11 Regression] coroutines: since 11.3 co_await attempts to copy a move-only value when await_transform(T &) exists
Summary: [11 Regression] coroutines: since 11.3 co_await attempts to copy a move-only ...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 11.3.0
: P2 normal
Target Milestone: 11.4
Assignee: Jason Merrill
URL:
Keywords: C++-coroutines, rejects-valid
Depends on:
Blocks:
 
Reported: 2022-04-27 12:28 UTC by Daniel Vrátil
Modified: 2023-08-11 19:32 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work: 11.2.0, 12.3.0
Known to fail: 11.3.0, 12.0
Last reconfirmed: 2022-04-27 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Vrátil 2022-04-27 12:28:38 UTC
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`.
Comment 1 Daniel Vrátil 2022-04-27 12:29:38 UTC
Link to godbolt: https://godbolt.org/z/MecdTEzMT
Comment 2 Jakub Jelinek 2022-04-27 13:12:45 UTC
Started with r12-618-g14ed21f8749ae359690d9c4a69ca38cc45d0d1b0
which has been backported to 11.3 in
r11-9055-gb874ece3ff95d3afa575d40b6e14e95cae8baf87
Comment 3 GCC Commits 2023-03-16 12:01:25 UTC
The trunk branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:d0ed0690f1e86621a5cb62eec1d144036feb16f8

commit r13-6711-gd0ed0690f1e86621a5cb62eec1d144036feb16f8
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 15 17:02:15 2023 -0400

    c++: co_await and move-only type [PR105406]
    
    Here we were building a temporary MoveOnlyAwaitable to hold the result of
    evaluating 'o', but since 'o' is an lvalue we should build a reference
    instead.
    
            PR c++/105406
    
    gcc/cp/ChangeLog:
    
            * coroutines.cc (build_co_await): Handle lvalue 'o'.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/coroutines/co-await-moveonly1.C: New test.
Comment 4 GCC Commits 2023-04-18 20:45:56 UTC
The releases/gcc-12 branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:6fd32842404ac1a3cd98189f61d93c5bc9c8680c

commit r12-9435-g6fd32842404ac1a3cd98189f61d93c5bc9c8680c
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 15 17:02:15 2023 -0400

    c++: co_await and move-only type [PR105406]
    
    Here we were building a temporary MoveOnlyAwaitable to hold the result of
    evaluating 'o', but since 'o' is an lvalue we should build a reference
    instead.
    
            PR c++/105406
    
    gcc/cp/ChangeLog:
    
            * coroutines.cc (build_co_await): Handle lvalue 'o'.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/coroutines/co-await-moveonly1.C: New test.
Comment 5 GCC Commits 2023-04-22 00:22:48 UTC
The releases/gcc-11 branch has been updated by Jason Merrill <jason@gcc.gnu.org>:

https://gcc.gnu.org/g:657fb0db2648a8cd7ba355fdec570fe2f08448ac

commit r11-10642-g657fb0db2648a8cd7ba355fdec570fe2f08448ac
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Mar 15 17:02:15 2023 -0400

    c++: co_await and move-only type [PR105406]
    
    Here we were building a temporary MoveOnlyAwaitable to hold the result of
    evaluating 'o', but since 'o' is an lvalue we should build a reference
    instead.
    
            PR c++/105406
    
    gcc/cp/ChangeLog:
    
            * coroutines.cc (build_co_await): Handle lvalue 'o'.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/coroutines/co-await-moveonly1.C: New test.
Comment 6 Jakub Jelinek 2023-05-29 10:06:52 UTC
GCC 11.4 is being released, retargeting bugs to GCC 11.5.
Comment 7 Jason Merrill 2023-08-11 19:32:48 UTC
Fixed in 11.4/12.3/13.