<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/130326>130326</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
Coroutine miscompile: parameter destroyed twice(?) at -O1
</td>
</tr>
<tr>
<th>Labels</th>
<td>
miscompilation,
coroutines
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
zmodem
</td>
</tr>
</table>
<pre>
I'm not actually sure that the parameter is destroyed twice, but that's how the bug first appeared.
Consider:
```
#include <coroutine>
#include <utility>
#include <stdio.h>
// Boilerplate based on https://theshoemaker.de/posts/yet-another-cpp-coroutine-tutorial
class Task {
public:
struct FinalAwaiter {
bool await_ready() const noexcept { return false; }
template <typename P> auto await_suspend(std::coroutine_handle<P> handle) noexcept {
return handle.promise().continuation;
}
void await_resume() const noexcept { }
};
struct Promise {
std::coroutine_handle<> continuation;
Task get_return_object() {
return Task { std::coroutine_handle<Promise>::from_promise(*this) };
}
void unhandled_exception() noexcept { }
void return_void() noexcept { }
std::suspend_always initial_suspend() noexcept { return {}; }
FinalAwaiter final_suspend() noexcept { return {}; }
};
using promise_type = Promise;
Task() = default;
~Task() { if (handle_) { handle_.destroy(); } }
struct Awaiter {
std::coroutine_handle<Promise> handle;
bool await_ready() const noexcept { return !handle || handle.done(); }
auto await_suspend(std::coroutine_handle<> calling) noexcept {
handle.promise().continuation = calling;
return handle;
}
void await_resume() const noexcept { }
};
auto operator co_await() noexcept { return Awaiter{handle_}; }
void run() {
handle_.promise().continuation = std::noop_coroutine();
handle_.resume();
}
private:
explicit Task(std::coroutine_handle<Promise> handle) : handle_(handle) { }
std::coroutine_handle<Promise> handle_;
};
// The interesting part starts here.
namespace {
struct Cleanup {
Cleanup() : is_engaged(true) {}
Cleanup(Cleanup&& other) { is_engaged = true; other.is_engaged = false; }
~Cleanup() { if (is_engaged) { printf("CLEANUP\n"); } }
bool is_engaged;
};
}
Task hello(Cleanup c) {
printf("HELLO\n");
co_return;
}
Task foo(Cleanup c) { co_await hello(std::move(c)); }
int main() {
Task t = foo(Cleanup());
t.run();
}
$ clang++ /tmp/a.cc -std=c++20 -O0 && ./a.out
HELLO
CLEANUP
$ clang++ /tmp/a.cc -std=c++20 -O1 && ./a.out
HELLO
CLEANUP
CLEANUP
```
The expectation is that there is only one "engaged" Cleanup object, and so we should only see that printed once.
The anonymous namespace seems to be an important factor.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJy0V8tu4zoS_Rp6U7AgU3HsLLyQnRgzQGO6Fz1rg5LKFqcpUiCLSXsW_e0XlChZdh79uLhGgEgiearq1KkiKZyTJ424YcstWz7OhKfa2M3_G1NhMytMdd78m_FVA9oQiJK8UOoMzlsEqgUB1QitsKJBQgvSQYWOrDljBfQiS2R8B4WnbjLjKwe1eekWFf4ER2kdgWhbFBarhKU5S_Od0U5WaFmW9x_YfRr_0pzxTOpS-QqBZbvSWONJamTZ06tBT1JJOr815KiSJqnjUBjdM76HrZEKbasEIRTCYQVGQ03UuuBLN4dqdLXBRnxDm1TI-L41jhzj-zPSXGhDNdp52bbz0bc5eTJWCsXSvFTCOfgq3Ddgqy2wNG99oWTZxwoA4Mj6kmAvtVD5i5CBVbbaxtHwK4xRIMLQwaKozoyvGX-A0mhHoA1-L7GlDt4ieavhKJRDlm2BrR4nOIRNHyrLdnRuUYsG4QvLnkB4MtGC865FXTG-dlQFL7N8DOxQC10pZNmuWxXf-MOVExOD4Rdd6ucmrTWNdNhHkJRGk9RekDSaZdOV144_G1mNBDjf4PsMXBaGpw7zmucvvQc3nn4Uawj1fU-71J4weBYCPZjif1hSdPBdOkZBfEhyJCt76qccrWkOUwZzqqXr7Ty-yV9HnNc9YnXoeQoh9O69w9xIeQwpPP98xRhJVNBBqBdxdiC1JCnURFivgCIpga0ukAH5qiaO4eVPUKZaCC_eSX2CyOMhFAKw7BFGtieaCWkaUpk9QoVH4RVdoH5MJ6y2II_A-Lqn-zB8jK9J7JP99Ohf9PFaojdd4NckMlRj9rc6B-OLHgfYasdWu6FuK6PxyvGJkd9uHl1BCaWkPr3VPH7aKrpcDADZhw0nDv8D7aSL2rRoBRkLpTl0gB_oMmaVrbaDPm5V2tec19fNY5DPT_gYCdfGtIfLPjkkbQo1jfpC4CjF1spnQXjZovB7q2QpaaiH31NkVzyj8bE-hvKI8f8W5iGmYpKTuKN_rRGkJrToqCtzYQkcCUsOarQYjhxh33OtKIdNIJbdTqHQvh2bdnwf6z8H6Q6oT-KEQeFk_RBDDOGyYHy6Z_weuiPC2CJGjC5tHUq27eckN4O3-_iPG5fGjjN1rP_eWqnp2E3ku09P-X_--4Utd5px_qr5xDYxwbghd9BFt2nVqJS5hAjldJ-bWv3X06dPn69shimliRvlW9hH8xbyWFyj7VErjXkOMg4zr1oTS3OpCRohb4qp33ipZ3dqLRbDpR4oGSvxxlXG76BUIjSvLeNbCIfEpmV8L5KyhHnn3GPZD_IU5p9TiEJIuknGE0vznp40H5LzZ9CLX4OeGJkerdM8lAt-b7GkvpNINx7yLYY3o9UZjEZgnI8S42OtDKedHQhdgTPwguBq41XVr3QYbw2dMrrzdYnJxbbQRp8b4x1citIhNg7IQBHGQTatsSQ0wVGUZGwyqzZZ9ZA9iBluFqu7RbbKsuViVm9Sjsv18XhcpVVRLO-W67Uo-FIIUfBVJe7FTG54ypdplq4W67uHxSJZLBbZ6v5uUa7vM7HMOLtLsRFSJUo9N4mxp5l0zuNmkaUZv58pUaBy3cWJ80a60jStVP2hMIh8xzgf25cLn5aPM7sJYPPCnxy7S5V05C7wJEnhZjesgRE0dN_JNevVHWvNsn3QtaCggpm3anN9bzlJqn2RlKZhfB_sxX_z1po-Z_sutnCTieE9b_hfAQAA___7vUix">