Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LWG-3460 Unimplementable noop_coroutine_handle guarantees #1452

Merged
merged 8 commits into from
Dec 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 49 additions & 11 deletions stl/inc/coroutine
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@ struct coroutine_handle<void> {
__builtin_coro_destroy(_Ptr);
}

protected:
private:
void* _Ptr = nullptr;
};

template <class _Promise>
struct coroutine_handle : coroutine_handle<> {
using coroutine_handle<>::coroutine_handle;
struct coroutine_handle {
constexpr coroutine_handle() noexcept = default;
constexpr coroutine_handle(nullptr_t) noexcept {}

_NODISCARD static coroutine_handle from_promise(_Promise& _Prom) noexcept { // strengthened
const auto _Prom_ptr = const_cast<void*>(static_cast<const volatile void*>(_STD addressof(_Prom)));
Expand All @@ -109,15 +110,46 @@ struct coroutine_handle : coroutine_handle<> {
return *this;
}

_NODISCARD constexpr void* address() const noexcept {
return _Ptr;
}

_NODISCARD static constexpr coroutine_handle from_address(void* const _Addr) noexcept { // strengthened
coroutine_handle _Result;
_Result._Ptr = _Addr;
return _Result;
}

constexpr operator coroutine_handle<>() const noexcept {
return coroutine_handle<>::from_address(_Ptr);
}

constexpr explicit operator bool() const noexcept {
return _Ptr != nullptr;
}

_NODISCARD bool done() const noexcept { // strengthened
return __builtin_coro_done(_Ptr);
}

void operator()() const {
__builtin_coro_resume(_Ptr);
}

void resume() const {
__builtin_coro_resume(_Ptr);
}

void destroy() const noexcept { // strengthened
__builtin_coro_destroy(_Ptr);
}

_NODISCARD _Promise& promise() const noexcept { // strengthened
return *reinterpret_cast<_Promise*>(__builtin_coro_promise(_Ptr, 0, false));
}

private:
void* _Ptr = nullptr;
};

_NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept {
Expand Down Expand Up @@ -145,9 +177,13 @@ struct noop_coroutine_promise {};

// STRUCT coroutine_handle<noop_coroutine_promise>
template <>
struct coroutine_handle<noop_coroutine_promise> : coroutine_handle<> {
struct coroutine_handle<noop_coroutine_promise> {
CaseyCarter marked this conversation as resolved.
Show resolved Hide resolved
friend coroutine_handle noop_coroutine() noexcept;

constexpr operator coroutine_handle<>() const noexcept {
return coroutine_handle<>::from_address(_Ptr);
}

constexpr explicit operator bool() const noexcept {
return true;
}
Expand All @@ -159,17 +195,19 @@ struct coroutine_handle<noop_coroutine_promise> : coroutine_handle<> {
constexpr void resume() const noexcept {}
constexpr void destroy() const noexcept {}

using _Promise = noop_coroutine_promise;

_NODISCARD _Promise& promise() const noexcept {
_NODISCARD noop_coroutine_promise& promise() const noexcept {
// Returns a reference to the associated promise
return *reinterpret_cast<_Promise*>(__builtin_coro_promise(_Ptr, 0, false));
return *reinterpret_cast<noop_coroutine_promise*>(__builtin_coro_promise(_Ptr, 0, false));
}

private:
coroutine_handle() noexcept {
_Ptr = __builtin_coro_noop();
_NODISCARD constexpr void* address() const noexcept {
return _Ptr;
}

private:
coroutine_handle() noexcept = default;

void* _Ptr = __builtin_coro_noop();
};

// ALIAS noop_coroutine_handle
Expand Down
50 changes: 50 additions & 0 deletions tests/std/tests/P0912R5_coroutine/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,54 @@ Task triangular_number(const int n) {
co_return n + co_await triangular_number(n - 1);
}

void test_noop_handle() { // Validate noop_coroutine_handle
const noop_coroutine_handle noop = noop_coroutine();
static_assert(noexcept(noop_coroutine()));

const coroutine_handle<> as_void = noop;
static_assert(noexcept(static_cast<coroutine_handle<>>(noop_coroutine())));

assert(noop);
assert(as_void);
static_assert(noexcept(static_cast<bool>(noop)));
static_assert(noexcept(static_cast<bool>(as_void)));

assert(!noop.done());
assert(!as_void.done());
static_assert(noexcept(noop.done()));
static_assert(noexcept(as_void.done()));

assert(noop);
assert(as_void);
noop();
as_void();
static_assert(noexcept(noop()));

assert(noop);
assert(as_void);
noop.resume();
as_void.resume();
static_assert(noexcept(noop.resume()));

assert(noop);
assert(as_void);
noop.destroy();
as_void.destroy();
static_assert(noexcept(noop.destroy()));

assert(noop);
assert(as_void);
assert(&noop.promise() != nullptr);
static_assert(noexcept(noop.promise()));

assert(noop);
assert(as_void);
assert(noop.address() != nullptr);
assert(noop.address() == as_void.address());
static_assert(noexcept(noop.address()));
static_assert(noexcept(as_void.address()));
}

int main() {
assert(g_tasks_destroyed == 0);

Expand Down Expand Up @@ -126,6 +174,8 @@ int main() {
const hash<coroutine_handle<>> h;
(void) h(coroutine_handle<>{});
}

test_noop_handle();
}

#else // ^^^ test <coroutine> ^^^ / vvv don't test <coroutine> vvv
Expand Down