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

    <tr>
        <th>Summary</th>
        <td>
            clang may allocate exception thrown from a coroutine in place of another coroutine promise where both are allocated somewhere on the stack
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

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

    <tr>
      <th>Reporter</th>
      <td>
          pubg-hacks
      </td>
    </tr>
</table>

<pre>
    I encountered this with clang-cl 14.0.6 on windows and it only happens under max speed optimizations (/O2 for msvc) msvc never does this neither clang under other optimizations mode. I'm still trying to construct a minimal reproducible bug code but it is hard due to being an optimizer bug ! but I'll provide an explanatory code:

spawn function:
```
        template<NonAwaitableFunctor<spawner&> Fn>
        auto scoped_spawn(any_executor& ex, Fn fn) -> task<std::invoke_result_t<Fn, spawner&>>
        {
                using fn_result = std::invoke_result_t<Fn, spawner&>;
                constexpr bool returns_void = std::is_same_v<fn_result, void>;
                using result_t = std::conditional_t<returns_void, std::monostate, fn_result>;

                spawner s{ ex };
                printf("coroutine fram ptr: %p\n", &s);
                std::optional<std::exception_ptr> ex_ptr;
                std::optional<result_t> result;
                
                try
                {
                        if constexpr (returns_void)
                        {
                                printf("executing fn(s)\n");
                                fn(s);
                                printf("executed fn(s)\n");
                        }
                        else
                        {
                                result.emplace(fn(s));
                        }
                }
                catch (const std::exception& ex) // ex instead of ... to see the the address
                {
                        printf("caught an exception from fn(s): %p\n", std::addressof(ex));
                        ex_ptr.emplace(std::current_exception());
                        printf("stored an exception from fn(s)\n");
                }
                
                printf("waiting for spawner\n");
                co_await s.wait();
                printf("waited for spawner\n");
                if (ex_ptr)
                        std::rethrow_exception(*ex_ptr);

                if constexpr (!returns_void)
                        co_return std::move(*result);
        }
```

test:

        ```
        task<> spawned_task(int inc, int& n)
        {
                n += inc;
                co_return;
        }

        task<> test_scoped_spawn(any_executor& ex)
        {
               int count = 0;
                auto throwing_fn = [&](spawner& s)
                {
                        s.spawn(spawned_task(2, count)); // thrown exception is always 56 bytes after 
                        s.spawn(spawned_task(3, count));
                        s.spawn(spawned_task(5, count));
                        throw std::exception{ "" };
                };
               TEST_THROW_EX(co_await scoped_spawn(ex, throwing_fn), std::exception, "scoped_spawn didn't propagate the exception");
                TEST_EQ(count, 10, "count", "10");
        }
```

sample output:

```
main stack address: 000000C3E371F608 <= address of a variable in the first line in main test method
... skip some lines
coroutine fram ptr: 0000029164948D68
executing fn(s)
task(task&&: 000002916496AF88) coroutine(000000C3E371E448) promise(000000C3E371E458) <= allocated on the stack !!
task(task&&: 000002916496B7C8) coroutine(000000C3E371E3F8) promise(000000C3E371E408)
task(task&&: 000002916496B048) coroutine(000000C3E371E3A8) promise(000000C3E371E3B8)
caught an exception from fn(s): 000000C3E371E498 <= allocated on the stack near the first promise !!
stored an exception from fn(s)
waiting for spawner
await_ready : 3
await_suspend (0000029164948D90)
waiter address: 0000029164948D30
~await_suspend (0000029164948D90)
```

where does it crash ?
after coroutine is suspended in wait point the executor executes the three spawned coroutines I step in the debugger on calling handle.resume() of first task and the promise content in the debugger is invalid !! it seems to be overwritten by the exception data so when the coroutine resumes it causes random errors of access violation in very different addresses than code address !

I added a dummy reference to the promise in the destructor of the task with printf and it removes the issue also doing so in the task constructor used by the promise removes the issue and the exception is allocated in different memory !
 
I think the optimizer is thinking that the promise is not referenced anymore since the task isn't co_await ed but the handle is resumed() instead so it allocates the exception in place of it ??
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJyVWElv47gS_jXKhWhDkbwefMhiY_oyg_deA29uBi3SNicSKZCU3Z5fP1VF7XbSnsCJFZL11b6IeyOu6-9M6sxU2ksrBfMn5dhF-RPLcq6P37KcPU8n8WTOjIZ1LczFMa4FUx5W8is78bKU2rFKC2lZwX8yV0oAMqVXhfqbe2VgN0qWUbL9I2EHA4fcOYuSFX0zLc9AJ4x0gbeWwBxWiH2NamhliFgYISfse5QsCua8ynPm7VUBiTcsgwPeVplnnBVKq4LnzMrSGlFlap9Ltq-OcEjgg0dNgO-JW8FEJZF-LxGI64YlMEeKKHkmAmQK_ADvrAADzsmfJYjLvbFXwo3Slyh-j-L6ryv5RbNDpTOUvducx_WH_mX1j5cFoHkAefvd6JcLV56D0FskNxZWCU7aKJlH6YZtAXDTcFvxCuR3mSml2NE5sDzX1538KbMKyZM5SBslb0DHDhrd8A1RPHcfCO0Fipe-KH02H3Jnpatyv_OwBXyAasC7zzhavHbP8apyaMKDrhFYlL6zfws-ACSfgp3BFcagO31ltdudjRIjcLdzvJC7M8C2_BEdj97iBkEbUYZQwFModBnPSco-U5K3OVgYbZxHn8Fqx3TArGNZq8kc2AycwaLF-0io0irtD5Q0SWasqbzSkh0sL1jpIQReIBZnZTR7A7MlyBNM5sCXI5hWPoxj1KLvYfkzk7S8I8gNSBKefgnR-W3DGk0HRP1nyMrB1mJ0cqUOrPMtqDw08mp0-pZ-ZK4Q6CH4YIWs0thpbJ_w6c7d273FhuL2GDS4dbgicycf0ScYdUJ1IIOYWnb8HmE0_DfjPjuhYcnK7Nb_TUlYwaEtfDAkFTqEQxU_sMlkgjXRSSiNp_DLhQAR3dd-HcQwr44nHyplzRWi2RQ9Q97GdCtpzc4gGAl61wohfHtG69K4slZqv-tpvPwMpS-1g3oJzv5K6k_dP_b9_dzG2k6hCl2xKX6fQWZmx_E8cxP8qnX4vG7gIQzVB6AhBcm0lP83KdfaETLzZM1lZMeXju5esRunNzTRrzMc9AwH-uX1LAOvppoPNWiNPWyo4a-Xzo_6cdNpP2vAoRdieQuWEztaSpZgXUiNDKMTHjFx9ECBYRJo0PcVGwqS3DgzKHlfj8_EQV12D3T31UjPRi42_EF1aPSjthePZKRRgjwOIbo7aDoUzV6xPc_eMQPads3c2I835cBNGoFHNqVcJynanGwKETHvZx_MaTy_8KtjsznbX8EcjB9gcGWPMktvmT1KOvslKYl7r8AuUCPQM7nT6vsrI_f82Pzvx-7Hb__94_-7zZ9UwJsKMIyAMM_1PEUCvt0t9TgqJH16JpSA9YXHebbkRxhiqMb3aO5UDBJt8x8Siizyxp7jGr1eqeeSBNcHCF1UfpW2MMOVMKnD6FNW4_wdERRcYbHg2UfbmaCbxPTzlm7SxfN2Hi8Z5dB7cwRbG2dnbhUO15AKpPVBWeiROU5bsELAmHOsgNJnRGCHDdF9qJI5U0g6W3fC-5MaiZGsnufT1XT5Pl-Gs_fGlFCuQrSFrzmNwgOM-ct2iZWftdzgdF_XzXRK--DOQrnb3RntNsbIcwMDAr6vBQMEM0KRxs9jAr0u3r4WKN1-KVC8fFz513j6C14vX_FKXztej80kQ1FXy18YTktue4FUizGw5wNTBZ27NxvQBtUAaB9cXBmKmPaXXQWv3_B63mjeBt4qHiBDzRznSns0bRJxsXkc9G4SX-C9XYaXeyhbmeUOJtF0WwtMlbtLGijuNSMwDyQelbrSYJMKBSn0uPqB7guwVFkYTOtS3YE59h08Issmr4WEF_gjXiJoloHv0LAnrkUuJzhSFGG8WGFRCJ7DEKR7DqRu3AiDjJc0AgxBQXJ4peU5voqSp1FbGJgLF24TGMwv9mKVB2poW8P6ygT3HGoJA2MF3M4kQbZgPF45eLIgE8SKtNbYUMOyDKvZWZmchx6pGXC7Ql0_HCTOvY2jyWIQdnTx0VTBNizD3--4gdHJRFUUVxCAMDK6FumbojVBuGgBt4As5BA0HF0ghWG0uSyyEqe44DTlXAUi5KC0MOgKeKgBiby9vwFY0Fo0NmuY38GqPTUaFZocVbpnjgKowT6t5qxR3Z-U_iCY7t5HubBMN0sn7odGcEwb3xkJs_oK4BCQimzWKKRcaLFtB0edqgAWwhCxgrdFHYrNKxjaxre6uLGamtH7DppfeUqudPsk1qlYpSv-5JXP5TrcpRX82sL0EOohi0oQ76djD5nrcAXX7TYmCBm-h23GreyZHJtj2OwXyKfK5uuT9yWWnTDlHSFWqv0kMwX8k-fn5usbsPhLZjBJbMnHUBm3s_l0Nns6rdMkTsXswDMxy-LnLJsmi-d0P0sPWXzYr5bLp5zv4WV7TdNqouUlhAkOIrP3J7VO4iSJF_HqeRUvk9kkFfPpPM7m80yIFEaFaBpL6P35BOWYGHt8smsSCfLdwWaunHfdJndOHbWUxA7wYXA-Gbsuq_3x2wmUdk_EfU3S_wOcCXeE">