Issue |
130326
|
Summary |
Coroutine miscompile: parameter destroyed twice(?) at -O1
|
Labels |
miscompilation,
coroutines
|
Assignees |
|
Reporter |
zmodem
|
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.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs