blob: dd6df99a56232408bc51f83a3458dd081c1661ee [file] [log] [blame]
// Test for PR56919. Tests the destroy function contains the call to delete function only.
//
// REQUIRES: x86-registered-target
//
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 %s -O3 -S -o - | FileCheck %s
#include "Inputs/coroutine.h"
namespace std {
template <typename T> struct remove_reference { using type = T; };
template <typename T> struct remove_reference<T &> { using type = T; };
template <typename T> struct remove_reference<T &&> { using type = T; };
template <typename T>
constexpr typename std::remove_reference<T>::type&& move(T &&t) noexcept {
return static_cast<typename std::remove_reference<T>::type &&>(t);
}
}
template <typename T>
class Task final {
public:
using value_type = T;
class promise_type final {
public:
Task<void> get_return_object() { return Task<void>(std::coroutine_handle<promise_type>::from_promise(*this)); }
void unhandled_exception();
std::suspend_always initial_suspend() { return {}; }
auto await_transform(Task<void> co) {
return await_transform(std::move(co.handle_.promise()));
}
auto await_transform(promise_type&& awaited) {
struct Awaitable {
promise_type&& awaited;
bool await_ready() { return false; }
std::coroutine_handle<> await_suspend(
const std::coroutine_handle<> handle) {
// Register our handle to be resumed once the awaited promise's coroutine
// finishes, and then resume that coroutine.
awaited.registered_handle_ = handle;
return std::coroutine_handle<promise_type>::from_promise(awaited);
}
void await_resume() {}
private:
};
return Awaitable{std::move(awaited)};
}
void return_void() {}
// At final suspend resume our registered handle.
auto final_suspend() noexcept {
struct FinalSuspendAwaitable final {
bool await_ready() noexcept { return false; }
std::coroutine_handle<> await_suspend(
std::coroutine_handle<> h) noexcept {
return to_resume;
}
void await_resume() noexcept {}
std::coroutine_handle<> to_resume;
};
return FinalSuspendAwaitable{registered_handle_};
}
private:
std::coroutine_handle<promise_type> my_handle() {
return std::coroutine_handle<promise_type>::from_promise(*this);
}
std::coroutine_handle<> registered_handle_;
};
~Task() {
// Teach llvm that we are only ever destroyed when the coroutine body is done,
// so there is no need for the jump table in the destroy function. Our coroutine
// library doesn't expose handles to the user, so we know this constraint isn't
// violated.
if (!handle_.done()) {
__builtin_unreachable();
}
handle_.destroy();
}
private:
explicit Task(const std::coroutine_handle<promise_type> handle)
: handle_(handle) {}
const std::coroutine_handle<promise_type> handle_;
};
Task<void> Qux() { co_return; }
Task<void> Baz() { co_await Qux(); }
Task<void> Bar() { co_await Baz(); }
// CHECK: _Z3Quxv.destroy:{{.*}}
// CHECK-NEXT: #
// CHECK-NEXT: jmp _ZdlPv
// CHECK: _Z3Bazv.destroy:{{.*}}
// CHECK-NEXT: #
// CHECK-NEXT: jmp _ZdlPv
// CHECK: _Z3Barv.destroy:{{.*}}
// CHECK-NEXT: #
// CHECK-NEXT: jmp _ZdlPv