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:
parent
8c3846e802
commit
2c2278f300
@ -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,76 +142,107 @@ _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 static coroutine_handle from_address(void* __a)
|
|
||||||
{
|
|
||||||
coroutine_handle __self;
|
|
||||||
__self._M_fr_ptr = __a;
|
|
||||||
return __self;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 17.12.3.5, promise accesss
|
constexpr void* address() const noexcept { return _M_fr_ptr; }
|
||||||
_Promise& promise() const
|
|
||||||
{
|
constexpr static coroutine_handle from_address(void* __a)
|
||||||
void* __t
|
{
|
||||||
= __builtin_coro_promise (this->_M_fr_ptr, __alignof(_Promise), false);
|
coroutine_handle __self;
|
||||||
return *static_cast<_Promise*>(__t);
|
__self._M_fr_ptr = __a;
|
||||||
}
|
return __self;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// [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
|
||||||
|
{
|
||||||
|
void* __t
|
||||||
|
= __builtin_coro_promise (_M_fr_ptr, __alignof(_Promise), false);
|
||||||
|
return *static_cast<_Promise*>(__t);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void* _M_fr_ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
/// [coroutine.noop]
|
/// [coroutine.noop]
|
||||||
struct noop_coroutine_promise
|
struct noop_coroutine_promise
|
||||||
@ -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>;
|
||||||
|
54
libstdc++-v3/testsuite/18_support/coroutines/lwg3460.cc
Normal file
54
libstdc++-v3/testsuite/18_support/coroutines/lwg3460.cc
Normal 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>> );
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user