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. Tested powerpc64le-linux. Committed to trunk.
commit 2c2278f300cdd5f3181fe7df4dd1d869a67266a9 Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue Oct 20 11:18:35 2020 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. diff --git a/libstdc++-v3/include/std/coroutine b/libstdc++-v3/include/std/coroutine index 468d1107557..6e1cf141579 100644 --- a/libstdc++-v3/include/std/coroutine +++ b/libstdc++-v3/include/std/coroutine @@ -87,7 +87,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION coroutine_handle<void> { public: - // 17.12.3.1, construct/reset + // [coroutine.handle.con], construct/reset constexpr coroutine_handle() noexcept : _M_fr_ptr(0) {} constexpr coroutine_handle(std::nullptr_t __h) noexcept @@ -101,7 +101,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } public: - // 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) noexcept @@ -112,7 +112,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } public: - // 17.12.3.3, observers + // [coroutine.handle.observers], observers constexpr explicit operator bool() const noexcept { return bool(_M_fr_ptr); @@ -120,7 +120,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION 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 resume() const { __builtin_coro_resume(_M_fr_ptr); } @@ -131,10 +131,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION void* _M_fr_ptr; }; - // 17.12.3.6 Comparison operators - /// [coroutine.handle.compare] - constexpr bool operator==(coroutine_handle<> __a, - coroutine_handle<> __b) noexcept + // [coroutine.handle.compare], comparison operators + + constexpr bool + operator==(coroutine_handle<> __a, coroutine_handle<> __b) noexcept { return __a.address() == __b.address(); } @@ -142,76 +142,107 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if _COROUTINES_USE_SPACESHIP constexpr strong_ordering 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 // These are to enable operation with std=c++14,17. - constexpr bool operator!=(coroutine_handle<> __a, - coroutine_handle<> __b) noexcept + constexpr bool + operator!=(coroutine_handle<> __a, coroutine_handle<> __b) noexcept { return !(__a == __b); } - constexpr bool operator<(coroutine_handle<> __a, - coroutine_handle<> __b) noexcept + constexpr bool + operator<(coroutine_handle<> __a, coroutine_handle<> __b) noexcept { return less<void*>()(__a.address(), __b.address()); } - constexpr bool operator>(coroutine_handle<> __a, - coroutine_handle<> __b) noexcept + constexpr bool + operator>(coroutine_handle<> __a, coroutine_handle<> __b) noexcept { return __b < __a; } - constexpr bool operator<=(coroutine_handle<> __a, - coroutine_handle<> __b) noexcept + constexpr bool + operator<=(coroutine_handle<> __a, coroutine_handle<> __b) noexcept { return !(__a > __b); } - constexpr bool operator>=(coroutine_handle<> __a, - coroutine_handle<> __b) noexcept + constexpr bool + operator>=(coroutine_handle<> __a, coroutine_handle<> __b) noexcept { return !(__a < __b); } #endif template <typename _Promise> - struct coroutine_handle : coroutine_handle<> + struct coroutine_handle { - // 17.12.3.1, construct/reset - using coroutine_handle<>::coroutine_handle; + // [coroutine.handle.con], construct/reset - 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; __self._M_fr_ptr - = __builtin_coro_promise((char*) &p, __alignof(_Promise), true); + = __builtin_coro_promise((char*) &__p, __alignof(_Promise), true); 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; } - // 17.12.3.2, export/import - constexpr static coroutine_handle from_address(void* __a) - { - coroutine_handle __self; - __self._M_fr_ptr = __a; - return __self; - } + // [coroutine.handle.export.import], export/import - // 17.12.3.5, promise accesss - _Promise& promise() const - { - void* __t - = __builtin_coro_promise (this->_M_fr_ptr, __alignof(_Promise), false); - return *static_cast<_Promise*>(__t); - } - }; + constexpr void* address() const noexcept { return _M_fr_ptr; } + + constexpr static coroutine_handle from_address(void* __a) + { + coroutine_handle __self; + __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] struct noop_coroutine_promise @@ -231,35 +262,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // 17.12.4.1 Class noop_coroutine_promise /// [coroutine.promise.noop] 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: - // 17.12.4.2.1, observers + // [coroutine.handle.noop.observers], observers constexpr explicit operator bool() const noexcept { return true; } constexpr bool done() const noexcept { return false; } - // 17.12.4.2.2, resumption + // [coroutine.handle.noop.resumption], resumption void operator()() const noexcept {} void resume() const noexcept {} void destroy() const noexcept {} - // 17.12.4.2.3, promise access - _Promise& promise() const - { - return *static_cast<_Promise*>( - __builtin_coro_promise(this->_M_fr_ptr, __alignof(_Promise), false)); - } + // [coroutine.handle.noop.promise], promise access + noop_coroutine_promise& promise() const noexcept + { return __noop_coro_fr.__p; } + + // [coroutine.handle.noop.address], address + constexpr void* address() const noexcept { return _M_fr_ptr; } - // 17.12.4.2.4, address 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>; diff --git a/libstdc++-v3/testsuite/18_support/coroutines/lwg3460.cc b/libstdc++-v3/testsuite/18_support/coroutines/lwg3460.cc new file mode 100644 index 00000000000..14a94ded95b --- /dev/null +++ b/libstdc++-v3/testsuite/18_support/coroutines/lwg3460.cc @@ -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>> ); +}