Skip to content

Invalid exception object catch from nested lambda coroutines with clang 20.1.0 on windows #143235

Open
@ShirenY

Description

@ShirenY

The code below will trigger the assert(err == message) on windows with clang 20.1.0. While it will pass with gcc and msvc. Also the clang on linux does not have this issue.
This issue looks like #59723, which should had been fixed on the 20.1.0.

#include <cassert>
#include <coroutine>
#include <exception>
#include <iostream>
#include <stdexcept>
#include <string>

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

    struct promise_type
    {
        std::exception_ptr exception;
        handle_type        continuation;

        Task get_return_object()
        {
            return Task{handle_type::from_promise(*this)};
        }

        std::suspend_always initial_suspend() noexcept
        {
            return {};
        }
        auto final_suspend() noexcept
        {
            struct FinalAwaiter
            {
                bool await_ready() noexcept
                {
                    return false;
                }

                void await_resume() noexcept
                {
                }

                std::coroutine_handle<> await_suspend(handle_type h) noexcept
                {
                    auto& promise = h.promise();
                    return promise.continuation ? std::coroutine_handle<>(promise.continuation) : std::noop_coroutine();
                }
            };
            return FinalAwaiter{};
        }

        void unhandled_exception()
        {
            exception = std::current_exception();
        }

        void return_void()
        {
        }
    };

    handle_type handle;

    explicit Task(handle_type h) : handle(h)
    {
    }

    ~Task()
    {
        if (handle)
            handle.destroy();
    }

    bool await_ready() const noexcept
    {
        return false;
    }

    handle_type await_suspend(std::coroutine_handle<> continuation) noexcept
    {
        handle.promise().continuation =
            handle_type::from_address(continuation.address());
        return handle;
    }

    void await_resume()
    {
        rethrowIfAny();
    }

    void start()
    {
        if (handle && !handle.done())
        {
            handle.resume();
        }
    }

    void rethrowIfAny()
    {
        if (handle.promise().exception)
        {
            // auto except                = handle.promise().exception;
            // handle.promise().exception = nullptr;
            std::rethrow_exception(handle.promise().exception);
        }
    }
};

int main()
{
    constexpr static char message[] = "test a very long message ~~~~~~~";

    auto taskLambda = [&]() -> Task {
        try
        {
            co_await []() -> Task {
                co_await []() -> Task {
                    throw std::runtime_error(message);
                }();
            }();
        }
        catch (std::runtime_error e)
        {
            const std::string err = e.what();
            std::cout << err << std::endl;
            assert(err == message);
        }
    };

    Task task = taskLambda();
    task.start();

    return 0;
}

Below is the command line execution record.

C:\Users\YXX\source\repos\TestNestedCoroutine>clang++ --version
clang version 20.1.0
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin

C:\Users\YXX\source\repos\TestNestedCoroutine>clang++ -std=c++20 -O0 -g -o test.exe TestNestedCoroutine.cpp

C:\Users\YXX\source\repos\TestNestedCoroutine>test.exe
Unknown exception
Assertion failed: err == message, file TestNestedCoroutine.cpp, line 124

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions