201 lines · plain
1// RUN: rm -rf %t2// RUN: split-file %s %t3 4// RUN: %clang_cc1 -std=c++20 %t/task.cppm -I%t -emit-reduced-module-interface -o %t/task.pcm5// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/user.cpp -verify -fsyntax-only6 7//--- coroutine.h8 9namespace std {10 11template <typename R, typename...> struct coroutine_traits {12 using promise_type = typename R::promise_type;13};14 15template <typename Promise = void> struct coroutine_handle;16 17template <> struct coroutine_handle<void> {18 static coroutine_handle from_address(void *addr) noexcept {19 coroutine_handle me;20 me.ptr = addr;21 return me;22 }23 void operator()() { resume(); }24 void *address() const noexcept { return ptr; }25 void resume() const { __builtin_coro_resume(ptr); }26 void destroy() const { __builtin_coro_destroy(ptr); }27 bool done() const { return __builtin_coro_done(ptr); }28 coroutine_handle &operator=(decltype(nullptr)) {29 ptr = nullptr;30 return *this;31 }32 coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}33 coroutine_handle() : ptr(nullptr) {}34// void reset() { ptr = nullptr; } // add to P0057?35 explicit operator bool() const { return ptr; }36 37protected:38 void *ptr;39};40 41template <typename Promise> struct coroutine_handle : coroutine_handle<> {42 using coroutine_handle<>::operator=;43 44 static coroutine_handle from_address(void *addr) noexcept {45 coroutine_handle me;46 me.ptr = addr;47 return me;48 }49 50 Promise &promise() const {51 return *reinterpret_cast<Promise *>(52 __builtin_coro_promise(ptr, alignof(Promise), false));53 }54 static coroutine_handle from_promise(Promise &promise) {55 coroutine_handle p;56 p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);57 return p;58 }59};60 61template <typename _PromiseT>62bool operator==(coroutine_handle<_PromiseT> const &_Left,63 coroutine_handle<_PromiseT> const &_Right) noexcept {64 return _Left.address() == _Right.address();65}66 67template <typename _PromiseT>68bool operator!=(coroutine_handle<_PromiseT> const &_Left,69 coroutine_handle<_PromiseT> const &_Right) noexcept {70 return !(_Left == _Right);71}72 73struct noop_coroutine_promise {};74 75template <>76struct coroutine_handle<noop_coroutine_promise> {77 operator coroutine_handle<>() const noexcept {78 return coroutine_handle<>::from_address(address());79 }80 81 constexpr explicit operator bool() const noexcept { return true; }82 constexpr bool done() const noexcept { return false; }83 84 constexpr void operator()() const noexcept {}85 constexpr void resume() const noexcept {}86 constexpr void destroy() const noexcept {}87 88 noop_coroutine_promise &promise() const noexcept {89 return *static_cast<noop_coroutine_promise *>(90 __builtin_coro_promise(this->__handle_, alignof(noop_coroutine_promise), false));91 }92 93 constexpr void *address() const noexcept { return __handle_; }94 95private:96 friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept;97 98 coroutine_handle() noexcept {99 this->__handle_ = __builtin_coro_noop();100 }101 102 void *__handle_ = nullptr;103};104 105using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;106 107inline noop_coroutine_handle noop_coroutine() noexcept { return noop_coroutine_handle(); }108 109struct suspend_always {110 bool await_ready() noexcept { return false; }111 void await_suspend(coroutine_handle<>) noexcept {}112 void await_resume() noexcept {}113};114struct suspend_never {115 bool await_ready() noexcept { return true; }116 void await_suspend(coroutine_handle<>) noexcept {}117 void await_resume() noexcept {}118};119 120} // namespace std121 122//--- task.cppm123module;124#include "coroutine.h"125export module task;126export template <typename T>127struct [[clang::coro_await_elidable]] Task {128 struct promise_type {129 struct FinalAwaiter {130 bool await_ready() const noexcept { return false; }131 132 template <typename P>133 std::coroutine_handle<> await_suspend(std::coroutine_handle<P> coro) noexcept {134 if (!coro)135 return std::noop_coroutine();136 return coro.promise().continuation;137 }138 void await_resume() noexcept {}139 };140 141 Task get_return_object() noexcept {142 return std::coroutine_handle<promise_type>::from_promise(*this);143 }144 145 std::suspend_always initial_suspend() noexcept { return {}; }146 FinalAwaiter final_suspend() noexcept { return {}; }147 void unhandled_exception() noexcept {}148 void return_value(T x) noexcept {149 value = x;150 }151 152 std::coroutine_handle<> continuation;153 T value;154 };155 156 Task(std::coroutine_handle<promise_type> handle) : handle(handle) {}157 ~Task() {158 if (handle)159 handle.destroy();160 }161 162 struct Awaiter {163 Awaiter(Task *t) : task(t) {}164 bool await_ready() const noexcept { return false; }165 void await_suspend(std::coroutine_handle<void> continuation) noexcept {}166 T await_resume() noexcept {167 return task->handle.promise().value;168 }169 170 Task *task;171 };172 173 auto operator co_await() {174 return Awaiter{this};175 }176 177private:178 std::coroutine_handle<promise_type> handle;179};180 181inline Task<int> callee() {182 co_return 1;183}184 185export inline Task<int> elidable() {186 co_return co_await callee();187}188 189namespace std {190 export using std::coroutine_traits;191 export using std::coroutine_handle;192 export using std::suspend_always;193}194 195//--- user.cpp196// expected-no-diagnostics197import task;198Task<int> test() {199 co_return co_await elidable();200}201