libstdc++: Remove inheritance from std::coroutine_handle<> [LWG 3460]

This removes the coroutine_handle<> base class from the primary template
and the noop_coroutine_promise explicit specialization. To preserve the
API various members are added, as they are no longer inherited from the
base class.

I've also tweaked some indentation and formatting, and replaced
subclause numbers from the standard with stable names like
[coroutine.handle.con].

libstdc++-v3/ChangeLog:

	* include/std/coroutine (coroutine_handle<_Promise>): Remove
	base class. Add constructors, conversions, accessors etc. as
	proposed for LWG 3460.
	(coroutine_handle<noop_coroutine_promise>): Likewise.
	* testsuite/18_support/coroutines/lwg3460.cc: New test.
This commit is contained in:
Jonathan Wakely 2020-10-20 11:18:35 +01:00
parent 8c3846e802
commit 2c2278f300
2 changed files with 144 additions and 55 deletions

View File

@ -87,7 +87,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
coroutine_handle<void> coroutine_handle<void>
{ {
public: public:
// 17.12.3.1, construct/reset // [coroutine.handle.con], construct/reset
constexpr coroutine_handle() noexcept : _M_fr_ptr(0) {} constexpr coroutine_handle() noexcept : _M_fr_ptr(0) {}
constexpr coroutine_handle(std::nullptr_t __h) noexcept constexpr coroutine_handle(std::nullptr_t __h) noexcept
@ -101,7 +101,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
public: public:
// 17.12.3.2, export/import // [coroutine.handle.export.import], export/import
constexpr void* address() const noexcept { return _M_fr_ptr; } constexpr void* address() const noexcept { return _M_fr_ptr; }
constexpr static coroutine_handle from_address(void* __a) noexcept constexpr static coroutine_handle from_address(void* __a) noexcept
@ -112,7 +112,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
} }
public: public:
// 17.12.3.3, observers // [coroutine.handle.observers], observers
constexpr explicit operator bool() const noexcept constexpr explicit operator bool() const noexcept
{ {
return bool(_M_fr_ptr); return bool(_M_fr_ptr);
@ -120,7 +120,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bool done() const noexcept { return __builtin_coro_done(_M_fr_ptr); } bool done() const noexcept { return __builtin_coro_done(_M_fr_ptr); }
// 17.12.3.4, resumption // [coroutine.handle.resumption], resumption
void operator()() const { resume(); } void operator()() const { resume(); }
void resume() const { __builtin_coro_resume(_M_fr_ptr); } void resume() const { __builtin_coro_resume(_M_fr_ptr); }
@ -131,10 +131,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
void* _M_fr_ptr; void* _M_fr_ptr;
}; };
// 17.12.3.6 Comparison operators // [coroutine.handle.compare], comparison operators
/// [coroutine.handle.compare]
constexpr bool operator==(coroutine_handle<> __a, constexpr bool
coroutine_handle<> __b) noexcept operator==(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ {
return __a.address() == __b.address(); return __a.address() == __b.address();
} }
@ -142,61 +142,70 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if _COROUTINES_USE_SPACESHIP #if _COROUTINES_USE_SPACESHIP
constexpr strong_ordering constexpr strong_ordering
operator<=>(coroutine_handle<> __a, coroutine_handle<> __b) noexcept operator<=>(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ return std::compare_three_way()(__a.address(), __b.address()); } {
return std::compare_three_way()(__a.address(), __b.address());
}
#else #else
// These are to enable operation with std=c++14,17. // These are to enable operation with std=c++14,17.
constexpr bool operator!=(coroutine_handle<> __a, constexpr bool
coroutine_handle<> __b) noexcept operator!=(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ {
return !(__a == __b); return !(__a == __b);
} }
constexpr bool operator<(coroutine_handle<> __a, constexpr bool
coroutine_handle<> __b) noexcept operator<(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ {
return less<void*>()(__a.address(), __b.address()); return less<void*>()(__a.address(), __b.address());
} }
constexpr bool operator>(coroutine_handle<> __a, constexpr bool
coroutine_handle<> __b) noexcept operator>(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ {
return __b < __a; return __b < __a;
} }
constexpr bool operator<=(coroutine_handle<> __a, constexpr bool
coroutine_handle<> __b) noexcept operator<=(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ {
return !(__a > __b); return !(__a > __b);
} }
constexpr bool operator>=(coroutine_handle<> __a, constexpr bool
coroutine_handle<> __b) noexcept operator>=(coroutine_handle<> __a, coroutine_handle<> __b) noexcept
{ {
return !(__a < __b); return !(__a < __b);
} }
#endif #endif
template <typename _Promise> template <typename _Promise>
struct coroutine_handle : coroutine_handle<> struct coroutine_handle
{ {
// 17.12.3.1, construct/reset // [coroutine.handle.con], construct/reset
using coroutine_handle<>::coroutine_handle;
static coroutine_handle from_promise(_Promise& p) constexpr coroutine_handle() noexcept { }
constexpr coroutine_handle(nullptr_t) noexcept { }
static coroutine_handle
from_promise(_Promise& __p)
{ {
coroutine_handle __self; coroutine_handle __self;
__self._M_fr_ptr __self._M_fr_ptr
= __builtin_coro_promise((char*) &p, __alignof(_Promise), true); = __builtin_coro_promise((char*) &__p, __alignof(_Promise), true);
return __self; return __self;
} }
coroutine_handle& operator=(std::nullptr_t) noexcept coroutine_handle& operator=(nullptr_t) noexcept
{ {
coroutine_handle<>::operator=(nullptr); _M_fr_ptr = nullptr;
return *this; return *this;
} }
// 17.12.3.2, export/import // [coroutine.handle.export.import], export/import
constexpr void* address() const noexcept { return _M_fr_ptr; }
constexpr static coroutine_handle from_address(void* __a) constexpr static coroutine_handle from_address(void* __a)
{ {
coroutine_handle __self; coroutine_handle __self;
@ -204,13 +213,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return __self; return __self;
} }
// 17.12.3.5, promise accesss // [coroutine.handle.conv], conversion
constexpr operator coroutine_handle<>() const noexcept
{ return coroutine_handle<>::from_address(address()); }
// [coroutine.handle.observers], observers
constexpr explicit operator bool() const noexcept
{
return bool(_M_fr_ptr);
}
bool done() const noexcept { return __builtin_coro_done(_M_fr_ptr); }
// [coroutine.handle.resumption], resumption
void operator()() const { resume(); }
void resume() const { __builtin_coro_resume(_M_fr_ptr); }
void destroy() const { __builtin_coro_destroy(_M_fr_ptr); }
// [coroutine.handle.promise], promise access
_Promise& promise() const _Promise& promise() const
{ {
void* __t void* __t
= __builtin_coro_promise (this->_M_fr_ptr, __alignof(_Promise), false); = __builtin_coro_promise (_M_fr_ptr, __alignof(_Promise), false);
return *static_cast<_Promise*>(__t); return *static_cast<_Promise*>(__t);
} }
private:
void* _M_fr_ptr = nullptr;
}; };
/// [coroutine.noop] /// [coroutine.noop]
@ -231,35 +262,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 17.12.4.1 Class noop_coroutine_promise // 17.12.4.1 Class noop_coroutine_promise
/// [coroutine.promise.noop] /// [coroutine.promise.noop]
template <> template <>
struct coroutine_handle<noop_coroutine_promise> : public coroutine_handle<> struct coroutine_handle<noop_coroutine_promise>
{ {
using _Promise = noop_coroutine_promise; // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 3460. Unimplementable noop_coroutine_handle guarantees
// [coroutine.handle.noop.conv], conversion
constexpr operator coroutine_handle<>() const noexcept
{ return coroutine_handle<>::from_address(address()); }
public: // [coroutine.handle.noop.observers], observers
// 17.12.4.2.1, observers
constexpr explicit operator bool() const noexcept { return true; } constexpr explicit operator bool() const noexcept { return true; }
constexpr bool done() const noexcept { return false; } constexpr bool done() const noexcept { return false; }
// 17.12.4.2.2, resumption // [coroutine.handle.noop.resumption], resumption
void operator()() const noexcept {} void operator()() const noexcept {}
void resume() const noexcept {} void resume() const noexcept {}
void destroy() const noexcept {} void destroy() const noexcept {}
// 17.12.4.2.3, promise access // [coroutine.handle.noop.promise], promise access
_Promise& promise() const noop_coroutine_promise& promise() const noexcept
{ { return __noop_coro_fr.__p; }
return *static_cast<_Promise*>(
__builtin_coro_promise(this->_M_fr_ptr, __alignof(_Promise), false)); // [coroutine.handle.noop.address], address
} constexpr void* address() const noexcept { return _M_fr_ptr; }
// 17.12.4.2.4, address
private: private:
friend coroutine_handle<noop_coroutine_promise> noop_coroutine() noexcept; friend coroutine_handle noop_coroutine() noexcept;
coroutine_handle() noexcept { this->_M_fr_ptr = (void*) &__noop_coro_fr; } coroutine_handle() = default;
void* _M_fr_ptr = (void*) &__noop_coro_fr;
}; };
using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>; using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>;

View File

@ -0,0 +1,54 @@
// Copyright (C) 2020 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++2a" }
// { dg-do compile { target c++2a } }
#include <coroutine>
void
test01()
{
// LWG 3460. Unimplementable noop_coroutine_handle guarantees
static_assert( std::is_convertible_v<std::noop_coroutine_handle&,
std::coroutine_handle<>> );
static_assert( ! std::is_convertible_v<std::noop_coroutine_handle&,
std::coroutine_handle<>&> );
static_assert( ! std::is_assignable_v<std::noop_coroutine_handle&,
std::coroutine_handle<>> );
std::noop_coroutine_handle h = std::noop_coroutine();
std::coroutine_handle<> h2 = h;
h2(); // no-op
h2.resume(); // no-op
h2.destroy(); // no-op
}
void
test02()
{
// LWG 3469. Precondition of coroutine_handle::promise may be insufficient
struct P1 { };
struct P2 { };
static_assert( ! std::is_assignable_v<std::coroutine_handle<P1>&,
std::coroutine_handle<>> );
static_assert( ! std::is_assignable_v<std::coroutine_handle<P1>&,
std::coroutine_handle<P2>> );
}