<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/186311>186311</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Changes to coroutine promise object lost in `return_value` if not used, when `-O1` or higher is enabled (since Clang 18)
</td>
</tr>
<tr>
<th>Labels</th>
<td>
clang
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
hach-que
</td>
</tr>
</table>
<pre>
Consider the following code:
```cpp
#include <string>
#include <coroutine>
#include <cstdio>
#include <memory>
#include <optional>
#define debug_promise_state(context, promise) \
printf("(%p) %s: promise memory_zero_test=%d has_immediate_result=%s\n", promise, context, promise->memory_zero_test, promise->has_immediate_result ? "yes" : "no")
template<typename T>
struct promise_base
{
int memory_zero_test;
bool has_immediate_result;
std::optional<T> immediate_result;
promise_base()
: memory_zero_test(0xdeadbeef)
, has_immediate_result()
, immediate_result()
{
}
};
template<typename T>
struct task
{
private:
promise_base<T>* source;
bool has_promise_reference;
public:
task()
: source()
, has_promise_reference(false)
{
}
task(promise_base<T>* source)
: source(source)
, has_promise_reference(true)
{
}
task(const task&) = delete;
task(task&&) = delete;
task& operator=(const task&) = delete;
task& operator=(task&&) = delete;
bool await_ready()
{
debug_promise_state("await_ready", this->source);
if (this->has_promise_reference &&
this->source->has_immediate_result) {
printf("task: await_ready with value reference\n");
return true;
} else {
printf("task: await_ready with heap reference\n");
return false;
}
}
void await_suspend(auto handle)
{
debug_promise_state("await_suspend", this->source);
handle.resume();
}
T await_resume()
{
debug_promise_state("await_resume", this->source);
if (this->has_promise_reference &&
this->source->has_immediate_result) {
printf("task: await_resume with value_ref\n");
return this->source->immediate_result.value();
} else {
printf("task: await_resume with heap_ref\n");
return 10;
}
}
};
template<typename T>
struct promise;
namespace std {
template<typename T>
struct coroutine_traits<task<T>>
{
using promise_type = ::promise<T>;
};
}
template<typename T>
struct promise : public promise_base<T>
{
promise() = default;
promise(const promise&) = delete;
promise(promise&&) = delete;
~promise() = default;
promise& operator=(const promise&) = delete;
promise& operator=(promise&&) = delete;
task<T> get_return_object()
{
debug_promise_state("get_return_object", this);
return task<T>(this);
}
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_value(T&& value)
{
printf("setting return_value with promise %p\n", this);
this->has_immediate_result = true;
this->immediate_result = value;
// uncomment this line to get the correct value path behaviour.
//printf("using immediate_result field %s\n", this->immediate_result ? "yes" : "no");
}
void unhandled_exception() {}
};
task<int> test()
{
co_return 5;
}
task<int> test2()
{
int blah = co_await test();
co_return blah;
}
int main()
{
auto task = test2();
return 0;
}
```
The correct behaviour is to emit "task: await_ready with value reference" because `has_immediate_result` is set to true, and then checked with await_ready. For some reason the optimizer is removing the assignment of `has_immediate_result` if it's not immediately used in `return_value`.
This happens on trunk ([on Godbolt](https://godbolt.org/z/Ko19Gqanx)) and all versions of Clang since Clang 18 (where it regressed). Clang 17 has the correct behaviour with the `-O1`.
Note the commented out `printf` call inside `return_value`. This code produces different results with `-O0`, and with the printf uncommented out. MSVC gets this correct with optimizations turned on (`/O2`).
## Clang trunk `-std=c++20 -O0` (correct)
```
(0x58b8bb19a2c0) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
(0x58b8bb19b310) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
setting return_value with promise 0x58b8bb19b310
(0x58b8bb19b310) await_ready: promise memory_zero_test=-559038737 has_immediate_result=yes
task: await_ready with value reference
(0x58b8bb19b310) await_resume: promise memory_zero_test=-559038737 has_immediate_result=yes
task: await_resume with value_ref
setting return_value with promise 0x58b8bb19a2c0
```
## Clang trunk `-std=c++20 -O1` (incorrectâť—)
```
(0x5d08d38cc2c0) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
(0x7fff1dfb23b0) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
setting return_value with promise 0x7fff1dfb23b0
(0x7fff1dfb23b0) await_ready: promise memory_zero_test=-559038737 has_immediate_result=no
task: await_ready with heap reference
(0x7fff1dfb23b0) await_suspend: promise memory_zero_test=-559038737 has_immediate_result=no
(0x7fff1dfb23b0) await_resume: promise memory_zero_test=-559038737 has_immediate_result=no
task: await_resume with heap_ref
setting return_value with promise 0x5d08d38cc2c0
```
## Clang trunk `-std=c++20 -O1` with `printf` call uncommented (correct)
```
(0x578c364ec2c0) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
(0x7fff28dec930) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
setting return_value with promise 0x7fff28dec930
using immediate_result field yes
(0x7fff28dec930) await_ready: promise memory_zero_test=-559038737 has_immediate_result=yes
task: await_ready with value reference
(0x7fff28dec930) await_resume: promise memory_zero_test=-559038737 has_immediate_result=yes
task: await_resume with value_ref
setting return_value with promise 0x578c364ec2c0
using immediate_result field yes
```
## MSVC with `/O2` (optimizations enabled, correct)
```
(000001DD0B7CF690) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
(000001DD0B7CAD30) get_return_object: promise memory_zero_test=-559038737 has_immediate_result=no
setting return_value with promise 000001DD0B7CAD30
(000001DD0B7CAD30) await_ready: promise memory_zero_test=-559038737 has_immediate_result=yes
task: await_ready with value reference
(000001DD0B7CAD30) await_resume: promise memory_zero_test=-559038737 has_immediate_result=yes
task: await_resume with value_ref
setting return_value with promise 000001DD0B7CF690
```
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzUWk-T2joS_zTiohrKSAPYBw4zEN5ha_cdNrVXSrbbWBsj-VnyJJPDfvatlmxjQDBM8vKSR01lQqnV_VPr1_9UI4yRewWwIvNnMt9MRGtL3axKkZUPf7QwSXX-ulprZWQODbUl0EJXlf4s1Z5mOgfCn0jkfhaR_8nqGr8yLlVWtTlQwtfGNlLtCf9wsZLpRrdWKgguGptLHVo5wEE3r6EVXVuplai6NbecQyEV0BzSdr-rG32QBnbGCguExZlWFr5Ywta0WyIsoWS-JtETpZTWjVS2ICwmjLl_57UTYHND-FO_h3pEu6_Q6J0FYwnfEDbPaSnMTh4OkEthYdeAaatuzZD5WjmlI8treonngfAPF-pPl0NWKOFbShh7BUMYowiWMKa0M5l451g41BX6ga_taw1KHIB-9K4ztmkz2xvZpcIA7lk-d36RygYO3a-mWldXzt6LGJsje_jT8crWaJuGtwzXMcKD15F0C_jBM156Ko6-5CDyFKA4E2frMMYLvWx9iWosdHQLWW6cmzYD6rd9bIX5NPi2buSLkw6e2LuIsCdqdNtkEPB4L99AAQ2oXgZVt2kls6NmZzfkw053yA1hCywuROUi55pDTizePtJ1OEGBG6Bs096NKdPKdFfBFi7C-YbmUIEdObmTHaTeEFxQXUMjrG5cyL_HxvnW2ybHHBCfhbS7BkT-eoWj-AknQ8LYyXaXnGwpDSaZo_fHNl0uKChC7OSCt0E9eDra5Q57ovtaKnNHPoF_npmde_jT-PD0s7QlfRFVC_RIij7nJkeH958GbNso6khzskiWGwqVgW_FUIKo3wXBB9M5hhB5X7TMO4OmNTWonLBYtFbTUqi8CrL_jusfdN1JAG9sird16PMGD0bbx8E9I9FvIqjf_7dlKMIfURRx3EPOCzTnSKZO3fkdfAeNj0CRx3finEVv0PddBbJvj_odKGJqkQE2Ed1x7lI0tJs72whpDUq64_oa5HvGwTutwTa3JwqqdInXdy0Dpm7r89mpBtbffTxX63yNDhb9MbShYYyP5aAQJ_3VUcSXneH71cpz3DGWvSr-vzdAnGsNV8N3wDpX8CbKcUXtmss9IKeRozud_heya33czRQUUDLkoYuo6CN3xDOfiQJJko774i4J7xS8QEOlklaKapTm8cRKw5cMaovIe0t4CEfEt5UWUn2zSld6Oi_0Oedjl0O772G3jpONAWsxyMZ6fLIZggIHruOkdOK4kdJxag9MQptAWe-3BMX9CU5z2Ljp3BK2pa3K9OEAyjpdtMIp02rkmBuVM900kNmuB6mFLWkKpXiRum2mF9rGbvGZ5wJYIaHK6dnseOMYt-a_APPcjbbKF_N852kgteoD3JPgLHl7WktlMbq6gaubL4dbz3QXLnR-TJRXtrPAfpw100qU7mYyvXOlaWyMX1pC-TNjbmQVUgUsuJYJwXiujHAMqju90YnS_tXDG_g4uvThpqk0SAo4SEvf06kyRlPIRIsxsIiCTcciQuUG6aY9wdmaCpUj-xTNSsg-Qe61j8xN6VY31OgDWhNGK0dWnMIP8is4vA0c9AtSEFf8G5GjuS5uYimotIQtDVXaHtlbvdLWQE6lws0nGWMRTXvPSUNLUdegDNWuCVefsGUj82et6G86T3VlyRzTfmltbTCbubDZ-6WpbvaEbb8Stv2HniW__SHUF7w-ljiHiKqiL9AYqVF_QdeVUHtqJPZ9_v-zGM19LqEBKi1tYN-AMZATlkx7kSVOmiehfbxl52VcIovo4ffZ8Wj_0ha6PS5XQE51a1Gsi_hFRDPEJ90rW8hH1Lkn0zlgWszbDAzNZeGYglDxAoxH4Kw7RnZUGHB5a8ec5XFM6T___Z81pizjk1h_MrevI4Wwzm8ICncpdzFoYvs7c7-T6fDYRhjv3NXd4SJ6cNVnkxH2TNgzi6hHSF0P4KwND1InAcXi6Ms8TuM0nSWCZWgnUL9vP8I9zOdJxOMlX157ilP63FbKZz_S1ts17wzKFXjjSf17gWGR6LPxHbnpDURuNPsxkEJj0_tc6ph0kbrvZu6sY65UPXc_MJJsSLK8QeI8inMeZ9lfQOJlURSzvEgZT382iU-gXIH3Z5LYAbv3FeYmnr4j_nHX8qeGSvDggbn9zkAZs_U7A6WvSWelblyE7qoCyzjji0f4qwKIxTlkCf8VAmiAEj3dnAp8ugye4OcXimuIfs1CMSbbfV4PhohrrPoA6HslpPtpVwVKpBX2mWv6VhzgZ7bZRM_L9XaR_OhIGFl72vzsWDgHcxXiTyf7DUy_It3POTVi3SRf8TzhiZjAarZccr5cMB5PytUiXaacCZ5EsXjkiyKbJVGezB8zsYhnYg4TuWIRW0R8xqNHHvF4youYPbIsnj0uCh7PCvIYwUHIalpVLwec3SbSmBZWs3jBZ7NJJVKojPujBMYyLDWEMTLfTJoVbnhI270hj1EljTVHFVbaClbrUqg9uJl7eO4djutZSyttbHgexSEWp9fW-JD8jJP0MNJR3dBS7ks_JXeRiyF9OkkSlkzaplqdTarSlm06zfSBsC2C7n491I32j4hb5wRD2Lbzw8uK_T8AAP__6Up8_w">