diff mbox series

[committed,1/2] libstdc++: Remove inheritance from std::coroutine_handle<> [LWG 3460]

Message ID 20201020103835.GA924362@redhat.com
State New
Headers show
Series [committed,1/2] libstdc++: Remove inheritance from std::coroutine_handle<> [LWG 3460] | expand

Commit Message

Jonathan Wakely Oct. 20, 2020, 10:38 a.m. UTC
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 <jwakely@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.

Comments

Jonathan Wakely Oct. 20, 2020, 10:39 a.m. UTC | #1
libstdc++: Define noop coroutine details private and inline [PR 95917]

This moves the __noop_coro_frame type, the __noop_coro_fr global
variable, and the __dummy_resume_destroy function from namespace scope,
replacing them with private members of the specialization
coroutine_handle<noop_coroutine_promise>.

The function and variable are also declared inline, so that they
generate no code unless used.

libstdc++-v3/ChangeLog:

         PR libstdc++/95917
         * include/std/coroutine (__noop_coro_frame): Replace with
         noop_coroutine_handle::__frame.
         (__dummy_resume_destroy): Define inline in __frame.
         (__noop_coro_fr): Replace with noop_coroutine_handle::_S_fr
         and define as inline.
         * testsuite/18_support/coroutines/95917.cc: New test.

Tested powerpc64le-linux. Committed to trunk.
diff mbox series

Patch

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>> );
+}