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

Reply via email to