https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121219

Iain Sandoe <iains at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |jason at gcc dot gnu.org

--- Comment #2 from Iain Sandoe <iains at gcc dot gnu.org> ---

So it seems that the presence of the return of the g_r_o_o_a_f is interfering
with NVRO and that is breaking an expected guarantee (copy elision).

I am somewhat concerned that there's no specific requirement that the
g_r_o_o_a_f should return the same type as g_r_o.  So it could be possible that
one requires conversion and one does not.

http://eel.is/c++draft/dcl.fct.def.coroutine#11

@jason - do we still have more tweaks needed to CWG2563?

====


To see the issue it is enough to include/exclude the g_r_o_o_a_f.


struct Task {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;

    struct promise_type {
        Task* task_;
        int result_;

        static void* operator new(std::size_t size) noexcept {
            void* p = ::operator new(size, std::nothrow);
            std::cerr << "operator new (no arg) " << size << " -> " << p <<
std::endl;  
            return p;
        }
        static void operator delete(void* ptr) noexcept {
            return ::operator delete(ptr, std::nothrow);
        }
#if 1 // change to 0 to fix crash
        static Task get_return_object_on_allocation_failure() noexcept {
            std::cerr << "get_return_object_on_allocation_failure" <<
std::endl;
            return Task(nullptr);
        }
#endif

        auto get_return_object() { 
            std::cerr << "get_return_object" << std::endl;
            return Task{handle_type::from_promise(*this)}; 
        }

        auto initial_suspend() { 
            std::cerr << "initial_suspend" << std::endl;
            return std::suspend_always{}; 
        }

        auto final_suspend() noexcept { 
            std::cerr << "final_suspend" << std::endl;
            return std::suspend_never{};  // Coroutine auto-destructs
        }

        ~promise_type() {
            if (task_) {
                std::cerr << "promise_type destructor: Clearing Task handle" <<
std::endl;
                task_->h_ = nullptr;
            }
        }

        void unhandled_exception() { 
            std::cerr << "unhandled_exception" << std::endl;
            std::terminate(); 
        }

        void return_value(int value) { 
            std::cerr << "return_value: " << value << std::endl;
            result_ = value;
            if (task_) {
                task_->result_ = value;
                task_->completed_ = true;
            }
        }
    };

    handle_type h_;
    int result_;
    bool completed_ = false;

    Task(handle_type h) : h_(h) {
        std::cerr << "Task constructor" << std::endl;
        if (h_) {
            h_.promise().task_ = this;  // Link promise to Task
        }
    }

    ~Task() { 
        std::cerr << "~Task destructor" << std::endl;
        // Only destroy handle if still valid (coroutine not completed)
        if (h_) {
            std::cerr << "Destroying coroutine handle" << std::endl;
            h_.destroy(); 
        }
    }

    bool done() const { 
        return completed_ || !h_ || h_.done(); 
    }

    void resume() { 
        std::cerr << "Resuming task" << std::endl;
        if (h_) h_.resume(); 
    }

    int result() const {
        if (!done()) throw std::runtime_error("Result not available");
        return result_;
    }
};

Reply via email to