<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/59723>59723</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            Clang mistakenly elides coroutine allocation resulting in a segfault and `stack-use-after-return` from `AddressSanitizer`
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          cppdev123
      </td>
    </tr>
</table>

<pre>
    The problem was submitted in https://github.com/llvm/llvm-project/issues/56513 and https://github.com/llvm/llvm-project/issues/56455 but there were no responses.
I thought it was specific to windows, but it turned out to happen also on linux with clang 15.0.6 with optimization level `-O3` the address sanitizer detects the access of stack after the coroutine returns.

Compile the following code with:
`clang++-15 clangcorobug.cpp -std=c++20 -O3 -g -fsanitize=address -lpthread -o corobug`

```
#include <atomic>
#include <thread>
#include <condition_variable>
#include <coroutine>
#include <variant>
#include <deque>
#include <cassert>

// executor and operation base

class bug_any_executor;

struct bug_async_op_base {
        void invoke() {
                invoke_operation();
        }

protected:

        ~bug_async_op_base() = default;

        virtual void invoke_operation() = 0;
};

class bug_any_executor {
        using op_type = bug_async_op_base;

public:

        virtual ~bug_any_executor() = default;

        // removing noexcept enables clang to find that the pointer has escaped
        virtual void post(op_type& op) noexcept = 0;

        virtual void wait() noexcept = 0;
};

class bug_thread_executor : public bug_any_executor {
        void work_thd() {
                while (!ops_.empty()) {
                        std::unique_lock<std::mutex> lock{ lock_ };
                        cv_.wait(lock, [this] { return !ops_.empty(); });

                        while (!ops_.empty()) {
                                bug_async_op_base* op = ops_.front();
                                ops_.pop_front();
                                op->invoke();
                        }
                }

                cv_.notify_all();
        }

        std::mutex lock_;
        std::condition_variable cv_;
        std::deque<bug_async_op_base*> ops_;
        std::thread thd_;

public:

        void start() {
                thd_ = std::thread(&bug_thread_executor::work_thd, this);
        }

        ~bug_thread_executor() {
                if (thd_.joinable())
                        thd_.join();
        }

        // although this implementation is not realy noexcept due to allocation but I have a real one that is and required to be noexcept
        virtual void post(bug_async_op_base& op) noexcept override {
                {
                        std::unique_lock<std::mutex> lock{ lock_ };
                        ops_.push_back(&op);
                }
                cv_.notify_all();
        }

        virtual void wait() noexcept override {
                std::unique_lock<std::mutex> lock{ lock_ };
                cv_.wait(lock, [this] { return ops_.empty(); });
        }
};

// task and promise

struct bug_final_suspend_notification {
        virtual std::coroutine_handle<> get_waiter() = 0;
};

class bug_task;

class bug_resume_waiter {
public:
        bug_resume_waiter(std::variant<std::coroutine_handle<>, bug_final_suspend_notification*> waiter) noexcept : waiter_{ waiter } {}

        constexpr bool await_ready() const noexcept { return false; }

        std::coroutine_handle<> await_suspend(std::coroutine_handle<>) noexcept {
                return waiter_.index() == 0 ? std::get<0>(waiter_) : std::get<1>(waiter_)->get_waiter();
        }

        constexpr void await_resume() const noexcept {}

private:
        std::variant<std::coroutine_handle<>, bug_final_suspend_notification*> waiter_;
};

class bug_task_promise {
        friend bug_task;
public:

        bug_task get_return_object() noexcept;

        constexpr std::suspend_always initial_suspend() noexcept { return {}; }

        bug_resume_waiter final_suspend() noexcept {
                return bug_resume_waiter{ waiter_ };
        }

        void unhandled_exception() noexcept {
                ex_ptr = std::current_exception();
        }

        constexpr void return_void() const noexcept {}

        void get_result() const {
                if (ex_ptr)
                        std::rethrow_exception(ex_ptr);
        }

        std::variant<std::monostate, std::exception_ptr> result_or_error() const noexcept {
                if (ex_ptr)
                        return ex_ptr;
                return {};
        }

private:
        std::variant<std::coroutine_handle<>, bug_final_suspend_notification*> waiter_;
        std::exception_ptr ex_ptr = nullptr;
};

class bug_task {
        friend bug_task_promise;
        using handle = std::coroutine_handle<>;
        using promise_t = bug_task_promise;

        bug_task(handle coro, promise_t* p) noexcept : this_coro{ coro }, this_promise{ p } {
                //printf("task(%p) coroutine(%p) promise(%p)\n", this, this_coro.address(), this_promise);
        }

public:

        using promise_type = bug_task_promise;

        bug_task(bug_task&& other) noexcept
                : this_coro{ std::exchange(other.this_coro, nullptr) }, this_promise{ std::exchange(other.this_promise, nullptr) } { 
                printf("task(task&&: %p) coroutine(%p) promise(%p)\n", this, this_coro.address(), this_promise); 
        }

        ~bug_task() {
                if (this_coro) {
                        //printf("~task(%p) coroutine(%p) promise(%p)\n", this, this_coro.address(), this_promise);
                        this_coro.destroy();
                }
        }

        constexpr bool await_ready() const noexcept {
                return false;
        }

        handle await_suspend(handle waiter) noexcept {
                assert(this_coro != nullptr && this_promise != nullptr);
                this_promise->waiter_ = waiter;
                return this_coro;
        }

        void await_resume() {
                return this_promise->get_result();
        }

        bool is_valid() const noexcept {
                return this_promise != nullptr && this_coro != nullptr;
        }

        void start_coro(bug_final_suspend_notification& w) noexcept {
                assert(this_promise != nullptr && this_coro != nullptr);
                this_promise->waiter_ = &w;
                this_coro.resume(); // never throws since all exceptions are caught by the promise
        }

private:
        handle this_coro;
        promise_t* this_promise;
};

bug_task bug_task_promise::get_return_object() noexcept {
        return { std::coroutine_handle<bug_task_promise>::from_promise(*this), this };
}

// spawn operation and spawner

template<class Handler>
class bug_spawn_op final : public bug_async_op_base, bug_final_suspend_notification {
        Handler handler;
        bug_task task_;

public:

        bug_spawn_op(Handler handler, bug_task&& t)
                : handler { handler }, task_{ std::move(t) } {}

        virtual void invoke_operation() override {
                printf("starting the coroutine\n");
                task_.start_coro(*this);
                printf("started the coroutine\n");
        }

        virtual std::coroutine_handle<> get_waiter() override {
                handler();
                return std::noop_coroutine();
        }
};

struct dummy_spawn_handler_t {
        constexpr void operator()() const noexcept {}
};

void bug_spawn(bug_any_executor& ex, bug_task&& t) {
        using op_t = bug_spawn_op<dummy_spawn_handler_t>;
        op_t* op = new op_t{ dummy_spawn_handler_t{}, std::move(t) };
        ex.post(*op);
}

class bug_spawner;

struct bug_spawner_awaiter {
        bug_spawner& s;
        std::coroutine_handle<> waiter;

        bug_spawner_awaiter(bug_spawner& s) : s{ s } {}

        bool await_ready() const noexcept;

        void await_suspend(std::coroutine_handle<> coro);

        void await_resume() {}
};

class bug_spawner {
        friend bug_spawner_awaiter;

        struct final_handler_t {
                bug_spawner& s;

                void operator()() {
                        s.on_spawn_finished();
                }
        };

public:

        bug_spawner(bug_any_executor& ex) : ex_{ ex } {}

        void spawn(bug_task&& t) {
                using op_t = bug_spawn_op<final_handler_t>;
                // move task into ptr
                op_t* ptr = new op_t(final_handler_t{ *this }, std::move(t));
                ++count_;
                ex_.post(*ptr); // ptr escapes here thus task escapes but clang can't deduce that unless post() is not noexcept
        }

        bug_spawner_awaiter wait() noexcept { return { *this }; }

        void on_spawn_finished()
        {
                if (!--count_ && awaiter_)
                {
                        auto a = std::exchange(awaiter_, nullptr);
                        a->waiter.resume();
                }
        }

private:

        bug_any_executor& ex_; // if bug_thread_executor& is used instead enables clang to detect the escape of the promise
        bug_spawner_awaiter* awaiter_ = nullptr;
        std::atomic<std::size_t> count_ = 0;
};

bool bug_spawner_awaiter::await_ready() const noexcept {
        return s.count_ == 0;
}

void bug_spawner_awaiter::await_suspend(std::coroutine_handle<> coro) {
        waiter = coro;
        s.awaiter_ = this;
}

template<std::invocable<bug_spawner&> Fn>
bug_task scoped_spawn(bug_any_executor& ex, Fn fn) {
        bug_spawner s{ ex };
        std::exception_ptr ex_ptr;

        try
        {
                fn(s);
        }
        catch (const std::exception& ex) // ex instead of ... to observe the address of ex
        {
                printf("caught an exception from fn(s): %p\n", std::addressof(ex));
                ex_ptr = std::current_exception();
        }

        co_await s.wait();
        if (ex_ptr)
                std::rethrow_exception(ex_ptr);
}

// forked task to start the coroutine from sync code

struct bug_forked_task_promise;

class bug_forked_task {
        friend struct bug_forked_task_promise;
        bug_forked_task() = default;
public:
        using promise_type = bug_forked_task_promise;
};

struct bug_forked_task_promise {
        bug_forked_task get_return_object() noexcept { return {}; }

        constexpr std::suspend_never initial_suspend() noexcept { return {}; }

        constexpr std::suspend_never final_suspend() noexcept { return {}; }

        void unhandled_exception() noexcept {
                std::terminate();
        }

        constexpr void return_void() const noexcept {}
};

// test case

bug_task bug_spawned_task(int id, int inc, std::atomic<int>& n) {
        int result = n += inc;
        std::string msg = "count in coro (" + std::to_string(id) + ") = " + std::to_string(result);
        printf("%s\n", msg.c_str());
        co_return;
}

// using bug_thread_executor& instead of bug_any_executor& resolves the problem
bug_forked_task run_coros(bug_any_executor& ex) {
        std::atomic<int> count = 0;
        auto throwing_fn = [&](bug_spawner& s) {
                int frame_ptr = 0;
                printf("frame ptr ptr: %p\n", std::addressof(frame_ptr));
                s.spawn(bug_spawned_task(1, 2, count)); // the coroutine frame is allocated on the stack !
                s.spawn(bug_spawned_task(2, 3, count));
                s.spawn(bug_spawned_task(3, 5, count));
               // commenting the following line hides the problem
                throw std::runtime_error{ "catch this !" }; // on windows allocated on the stack as required by msvc c++ abi
        };

        try {
                co_await scoped_spawn(ex, throwing_fn);
        }
        catch (const std::exception& ex) {
                printf("scoped_spawn propagated exception: %s\n", ex.what());
        }

        printf("count after scoped_spawn: %d\n", count.load());
}


int main() {
        int var = 0;
        bug_thread_executor ex;
        printf("stack address: %p\n", std::addressof(var));
        run_coros(ex);
        ex.start();
        ex.wait();
        return 0;
}
```
the run `./corobug` and you will get something like this `AddressSanitizer: stack-use-after-return on address 0x7fb1ba9f9338 at pc 0x7fb1bd4d3b58 bp 0x7fb1ba1efd40 sp 0x7fb1ba1efd38`
without the address sanitizer a segfault is received.

</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzEO01zo7qyv4ZsVHHZMLbjRRaJM6l3V3fx3p4S0BjdgMSRhJ2cxfz2V_oABAhM7p1zz9RUJgOt7lZ_d0tgIciFAjwH-9dg__aAG1kw_pzWdQbXXRg9JCz7ev6_AlDNWVJChW5YINEkFZESMkQoKqSsRRC9BOF7EL5fiCyaZJOyKgjfy_La_vNYc_YvSGUQvhMhGhBB-L4_7HcRwjT7j3D82O9R0kgkC-CAbuoHZYiDqBkVIDbB9i3YvvwDyYI1l0IiIs0eakhJTlIkGboRmrGbCMKzxkQkkg2nkCGm8DJU4LoGinApGGIUlYQ2n-hGZIHSEtML2u03283BPGG1JBX5E0uiIOEKJQoO28d_RsFhq3hEOMs4CIEEpkSSP4GjDCSkUpi3aapeshwJidMPhHMJXL9JGWeNJBQQB8VeuzPz88yqmpSgIXNWluxG6AWlLAPNlpKtgT5sNc9B-BqEr4-7vdmCQp40l01a1-hRyCyI3lIDEm7R4z8j9HhBj3nLchC9tbt4LGtZcMAZemTIYgkOW5c19V_z1_w3jAhNyyYDFERnLFlF0iD66XtpMM-8TBnNiJJyfMWc4KSEWUAruJn3ejmVM28z-KOZxYyFAO6stDDKiBF8QtpIxrWBsxq4MYkEC3Ch0xILgZLmEmP6FbeLgujVBRKSN6k0UOKLpjGrY4UIBccO7nRlRDnklX1AED4F4WnwNtiezLu448VAOaROwfHNJVtzpiwTst58WshfE15amtEbyiDHTSlHm1AsEi4bXCKH1TE7GsG2X3p8G6HxC2yw10Yo42d1LL9q0Ain3A6R1k1SKjMcb7Nl-NdEQSt2aw2BQ8WuiiHK4DOFWiKgylyFjR6SoZzQDMkC6yiGakao8voCCwQixTVkfgnWTMggfLIbDcIDYrXiqiM0kqUXyQ0TaXczt25BB8ZFHTVEL8gIc1lFhjTjH7EsMr-53goV0PS7HatFvIGqll_WaKfgwfakA9dLEL00lPzRQFyy9COIzt3jqpHwGUQ_kX5xfNX_xmi4P4srvcYbKxoNHZ5RsH-VBRHB_k3RtmEY-biLXjXSgXMNsH9_b8H25HG5F8RqrS2NJOeMSo9Xt381UM3q-D7gYxD9dGOJB6yPFadp6OiFSJkk-VeMy_JuvHFVqHVlFOSu6N5Pwz9Kr35QG8LPPvkpa1Bi8S60mU0WWbw2XiizFhJz6bdphUrra0RDQx88DmWAekc5I22C98T4y4fKnxRyZYaKr82_GNGBqTPDkcY7oDWKtMEPl6bu0mwjUtUlVEClyYVEIMok4oDLrz74ZA2omIjLkqU2ZzYS_QMV-AoIa2jEKJh4SYTOrxz-aAiHTC1MoMO1GDY95jAJoOwKnJMMJg75l8Ye46eNKOIEq9CjbEMzNgYduuD3ve1uIpjb_2_b7eowuyrGOluc5CxrkBKLD20yNWcVGdZiTpmVE4rLWDSiBprFWqrEWuMgjVkBOmHJFptxgWmmatKzEsMFZKy2CfzbVY5iePYlB9FUYFH3nE3C02kCHIRPHdNdBXy-sw_TIS1Jx4bUlsagpnixj2Ol1o5lreWpaaaMCgmfNUcJYyXCCj5W8czqH2kAB31vKjkudYGHFtLLjJ4MGbsxV0Rz8jgNOHAt2zJjt7whNIPPXvla_yiI3nvTuYDSwFajfWolpaFfxkC7MZBK12MbW3b9Xr7a-Vv5KguZFfCkPSBXLME1s7_cpOLVPhNbBx_oJecEaDb1q7l83gJqBzYajVlihhCDSDkt9HoBdxJod4fLG_4SiKhuut_0pAh3akwje69JT8PAQJQerB4rnYaHzkWnUXuaRpQFNdSoVRUcilLf0c3Rhs-4lnxYDqUN50DlGMe3bNnqSf2-1pTbTRg1C9XOuSu9NZNhf1omdXvhIAvOboO99IvWlcEeP6oYZUIqxwvPveA6Ihp_9BOZbcSMx8B5V_15JLF6Y9ZW7OvIZ0edoc6PFP7WmOFQGwgMObZIm7IcbPBOoFkKMG0Qcjkw0wmzn5Hx-3c7WWuRxrKbbczQGkWxIHyyZBUlJcUOk2olx7ODF120xxr2-KrXmJLLNCEdveMrqrtE7pqMrrhqTqjMtfmFlokg3NfGGNvJXP-sRdo9CfZnGoRh3_qce7Y2dgzZdiwjxpaHWzMhfyRid4K0Wsr9rwfdVMhiWAwNpDSWs2uhBaYXJQuNYdODhefOTHVL51XKHUSdlMa4dOZxWfSosN-d4v-_qlB0t-9trWy22e3E6J20TMz2199rt7b5btdmICRnX_65jNsR_o6a2hPj2_J6gZCNMuNq2j72NQdDSnau7qgKBeHOCc7IepYrthHIVDgusCqYu_omemt58ia13l7uV0GeOtovxTEz48pjmZbWHxHxFZdLRc4y2SWZemS-Yvd68mVd6-lehj6g23ob-DdZ_pYNBOHh5gPXXueqVAchM06gcNXnc5zdBBKEpoBwWaKuuBAIc0Ap1kePyZeZ7w8nDytKJOs4XkMcpPBBGJkvYLrSZZrVbJO52OwMVNVXfouFzJTSTwObc1bFbvR8aSecNiwO-4-R4RktiBrfqHPIhmlmngF3oSVUdalFezYl3P9o7nh3gNcXdnp1zGrTTE2ONYZjw3vV6EBelqYtAAde1alFC2rlyNnlNgifxugtc041IseVffTSQmst9r-bqkLz4mq3YlelKemUC3dGizNHfXPTRTf36pCiKrLBGXiXXCf-rbjdDOKQY1HRMhnIVlGZ3eu3p4BzAuiU50nx1t86WpSxOh5WJGtHonbemTVV9WVtyFKOhz4-arCNHtueckWPPaGs0XSW2w7jB-erBwSfM9Y7c9jb1emdO0Rn795GTZVa6xylUbhpdMrm_cvN1tzue-QSLnb43NgDhyB8GU3xh5Y0ij6wcAvAAsR4PPl1AwJoKYqZ4zOvhU7KoCnOlqZV2oBSO63U4WI-NqyqPj3H1n2BtXpCaztdz0HsUrk2b7gTHc11_2NpTehbbZqs4Xe7ZWX2QPNO6Tmn2jBqDTonlIgCslWdxDeTUWcfPqc2VgKfOq3A50IS0UWlEyKWAsHdWDAS9SgK9GeWypnNORGhkiFVRzpAbbToxkVtuAifxgRUG22SD1oIGB7Z65tXKWuojEev4DN2wklX4rblqB5l6SsjAun7cLJohNlL-zhppL14kmIahEeJMsia1J6nNrQEIVBH4tQe0k5HF95J9DgweW-XDAbbAxF5J9zGvGestmfH0-kH4e7x0cixbRVwf2gyNPexo-BGMoSHAzpnkNLjOc93GwpN32WMe4iVjfu0H3DkPXWv2DEHkvsu6Sg4IlAj9AVOIQFn0ytJ5lKiroeM5SCWe7sXb2p46eTsH6k6mai9AdgPfQX5E7R7olZ1dw5LdT7xBl1N4Dtjjra62vSkp9RnCxk_6W8nqwFHbYaP3tC49RObgZR1lTvDp9P6dEyoqjw1FyfPwzyjeHmnXVvU9SUiZTVkK4q2d4pyOt6JmzVFH_pXT-enuU_yr7kAoMg_LVyUOaVYpoUKEsYappSdbNXe6OzcheVos9koP2GJAH6FwdVelquFM4y5XYedC2DaDwyQ6oeRw72drvbTxN5zDDmW6zMbbyb5jUdsxrKR2PQh3V2ycHT03QMxb4ufM_6h-jPdHjMzaRrdi9aSU525vvc8UzkbPEuj_L7Ec2B9Zd5qrKcRyMLl0enNjYXziEWqc93ezMKJp7qbXzMQWnNWvXAmbgZpv-dI_C6Zeyfka4j8m4fe_cU_4BWh-hj3Lz7hnr0KBUKidHQffTAaNNG6M1pCJSL6EqL-jabDcNQmcmIu04cHNEkBap2Zcpu6AKlSN3rTuHxpQEiurL8SFzuhDXVeRoTa40gdRhUW50Yli80yxXFm4reqiMLW5xZXtDP403DG2sfsINwLJxpX4rJJ1fL-1mTkDk6s39yJbsbJ52q1Pun4Mi4HwcoriLY6S0qoel26bswbqidFYrk3Oy5VaUa5pjKb3C83RbMO8oRe4pwaee9fVU2xf5sbGYw_U5Ao57iCLnVtFwZ3GlL3PbpCWJctO_TepCk2boEz8oGdQqoxaxF0CNoiYZySFHdEtLdZQTUyGsZ83hOEu9WUNdFoSnk1Ar12P4cADf_Y7aSsqoB2A9j-06JSba8gmc_u2rMTzm69_HlDJanA3kbRfV9oajDT_YU77Zc23BrqjLbfZs0JEIv-7m_yhSpxTZH9cgnhhCyMMEz5ODa-vs4ZlrqmrHUs-zdVlguDb4cBJd4aX_T2eyzG2N1gBJ-bW4GlNxRNk8qgEtX-bL42G2zdEMkcIhp0UzKceeiMiaifyqErTKhnJqVeXbHHy33flsDnXEy2tmAP1VcGgSv2uL8bIk1NPRzlOnf7R2_8ZbEtJHz94_CzOGXSvKEoOGw3Qfjef0qnD7O-WINupCxVJYYEq0AWxgk_wLrPYfti9va_7XeF5t4oTj8eGwGPWrWP7WVq2vUq289jnuwSfMpPUfSEsER12j7MfmRRsn9CSd2B7SDPfmyRGD6Jnrp93IgsmPkc0_OpI0YCLrriVUGRQwrkCpn9kvEhe46yU3TCD_C8OxzD3eG4j_YPxfPhkJ7S_LDDSXI6HtP88CPdnY5hjg_58ZCG4QN5DrdhuAvD4-5p_7Tfb7bZFrKnPNv-SBPYbZPgxxYqTMpNWV6rDeOXB_0F6fP-dAyjhxInUIr2G1j-rD82TZqLCH5sSyKk6JdJIkt4PuspSUWExB9Ayy8EpY6EfeB3vl4wFYVSF6GuAJReg8PWryKleN3P-BR72D40vHz-D76aVdv-_wAAAP__zQgEGw">