Implement C++17 changes to std::atomic (P0558R1 partial)

Message ID 20170419165622.GA25047@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely April 19, 2017, 4:56 p.m.
This implements the significant changes from P0558R1 for C++17, adding
nested typedefs and making it ill-formed to do arithmetic on
std::atomic<T*> if !is_object<T>::value.

We currently support such arithmetic for std::atomic<void*>, and even
have a test checking it, so I've only made it ill-formed for C++17
mode. I'd like to deprecate that extension for GCC 8 and make it
ill-formed in all modes at some point. PR 69769 points out that we
also allow that arithmetic for pointers to functions, which is just
crazy.

The unimplemented parts of the paper are changing the non-member
functions to use the new std::atomic<T>::value_type and
std::atomic<T>::difference_type types for the parameters, so they are
non-deduced contexts. That would be too big a change to make now, so
we can revisit that for GCC 8 too.

	PR libstdc++/69769
	* include/bits/atomic_base.h (__atomic_base): Add new typedefs for
	C++17.
	* include/std/atomic (atomic<bool>, atomic<T>, atomic<T*>): Likewise.
	(atomic<T*>::operator++, atomic<T*>::operator--)
	(atomic<T*>::operator+=, atomic<T*>::operator-=)
	(atomic<T*>::fetch_add, atomic<T*>::fetch_sub): Add static assertion
	to enforce C++17 requirement on pointer arithmetic.
	* testsuite/29_atomics/atomic/60695.cc: Adjust dg-error lineno.
	* testsuite/29_atomics/atomic/69769.cc: New test.
	* testsuite/29_atomics/atomic/operators/pointer_partial_void.cc:
	Disable test for C++17.
	* testsuite/29_atomics/atomic/requirements/typedefs.cc: New test.
	* testsuite/29_atomics/atomic_integral/requirements/typedefs.cc: New
	test.

Tested powerpc64le-linux, with all -std=gnu++?? options.

I plan to commit this tomorrow.
commit 8b28c80f1c250d87d8a30a99a2f190f2e17a8981
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Apr 19 16:30:18 2017 +0100

    Implement C++17 changes to std::atomic (P0558R1 partial)
    
    	PR libstdc++/69769
    	* include/bits/atomic_base.h (__atomic_base): Add new typedefs for
    	C++17.
    	* include/std/atomic (atomic<bool>, atomic<T>, atomic<T*>): Likewise.
    	(atomic<T*>::operator++, atomic<T*>::operator--)
    	(atomic<T*>::operator+=, atomic<T*>::operator-=)
    	(atomic<T*>::fetch_add, atomic<T*>::fetch_sub): Add static assertion
    	to enforce C++17 requirement on pointer arithmetic.
    	* testsuite/29_atomics/atomic/60695.cc: Adjust dg-error lineno.
    	* testsuite/29_atomics/atomic/69769.cc: New test.
    	* testsuite/29_atomics/atomic/operators/pointer_partial_void.cc:
    	Disable test for C++17.
    	* testsuite/29_atomics/atomic/requirements/typedefs.cc: New test.
    	* testsuite/29_atomics/atomic_integral/requirements/typedefs.cc: New
    	test.

Patch

diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index e79ff67..93f85c3 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -237,6 +237,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _ITp>
     struct __atomic_base
     {
+#if __cplusplus > 201402L
+      using value_type = _ITp;
+      using difference_type = value_type;
+#endif
+
     private:
       typedef _ITp 	__int_type;
 
diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
index 5b252a4..47d3004 100644
--- a/libstdc++-v3/include/std/atomic
+++ b/libstdc++-v3/include/std/atomic
@@ -62,6 +62,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<>
   struct atomic<bool>
   {
+#if __cplusplus > 201402L
+    using value_type = bool;
+#endif
+
   private:
     __atomic_base<bool>	_M_base;
 
@@ -173,6 +177,10 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct atomic
     {
+#if __cplusplus > 201402L
+      using value_type = _Tp;
+#endif
+
     private:
       // Align 1/2/4/8/16-byte types to at least their size.
       static constexpr int _S_min_alignment
@@ -351,6 +359,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp>
     struct atomic<_Tp*>
     {
+#if __cplusplus > 201402L
+      using value_type = _Tp*;
+      using difference_type = ptrdiff_t;
+#endif
+
       typedef _Tp* 			__pointer_type;
       typedef __atomic_base<_Tp*>	__base_type;
       __base_type			_M_b;
@@ -379,51 +392,111 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       __pointer_type
       operator++(int) noexcept
-      { return _M_b++; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b++;
+      }
 
       __pointer_type
       operator++(int) volatile noexcept
-      { return _M_b++; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b++;
+      }
 
       __pointer_type
       operator--(int) noexcept
-      { return _M_b--; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b--;
+      }
 
       __pointer_type
       operator--(int) volatile noexcept
-      { return _M_b--; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b--;
+      }
 
       __pointer_type
       operator++() noexcept
-      { return ++_M_b; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return ++_M_b;
+      }
 
       __pointer_type
       operator++() volatile noexcept
-      { return ++_M_b; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return ++_M_b;
+      }
 
       __pointer_type
       operator--() noexcept
-      { return --_M_b; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return --_M_b;
+      }
 
       __pointer_type
       operator--() volatile noexcept
-      { return --_M_b; }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return --_M_b;
+      }
 
       __pointer_type
       operator+=(ptrdiff_t __d) noexcept
-      { return _M_b.operator+=(__d); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.operator+=(__d);
+      }
 
       __pointer_type
       operator+=(ptrdiff_t __d) volatile noexcept
-      { return _M_b.operator+=(__d); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.operator+=(__d);
+      }
 
       __pointer_type
       operator-=(ptrdiff_t __d) noexcept
-      { return _M_b.operator-=(__d); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.operator-=(__d);
+      }
 
       __pointer_type
       operator-=(ptrdiff_t __d) volatile noexcept
-      { return _M_b.operator-=(__d); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.operator-=(__d);
+      }
 
       bool
       is_lock_free() const noexcept
@@ -522,22 +595,42 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __pointer_type
       fetch_add(ptrdiff_t __d,
 		memory_order __m = memory_order_seq_cst) noexcept
-      { return _M_b.fetch_add(__d, __m); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.fetch_add(__d, __m);
+      }
 
       __pointer_type
       fetch_add(ptrdiff_t __d,
 		memory_order __m = memory_order_seq_cst) volatile noexcept
-      { return _M_b.fetch_add(__d, __m); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.fetch_add(__d, __m);
+      }
 
       __pointer_type
       fetch_sub(ptrdiff_t __d,
 		memory_order __m = memory_order_seq_cst) noexcept
-      { return _M_b.fetch_sub(__d, __m); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.fetch_sub(__d, __m);
+      }
 
       __pointer_type
       fetch_sub(ptrdiff_t __d,
 		memory_order __m = memory_order_seq_cst) volatile noexcept
-      { return _M_b.fetch_sub(__d, __m); }
+      {
+#if __cplusplus > 201402L
+	static_assert( is_object<_Tp>::value, "pointer to object type" );
+#endif
+	return _M_b.fetch_sub(__d, __m);
+      }
     };
 
 
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/60695.cc b/libstdc++-v3/testsuite/29_atomics/atomic/60695.cc
index b24daae..fd94338 100644
--- a/libstdc++-v3/testsuite/29_atomics/atomic/60695.cc
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/60695.cc
@@ -27,4 +27,4 @@  struct X {
   char stuff[0]; // GNU extension, type has zero size
 };
 
-std::atomic<X> a;  // { dg-error "not supported" "" { target *-*-* } 190 }
+std::atomic<X> a;  // { dg-error "not supported" "" { target *-*-* } 198 }
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/69769.cc b/libstdc++-v3/testsuite/29_atomics/atomic/69769.cc
new file mode 100644
index 0000000..9724ea3
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/69769.cc
@@ -0,0 +1,79 @@ 
+// Copyright (C) 2017 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++17" }
+// { dg-do compile { target c++1z } }
+
+#include <atomic>
+
+void
+test01()
+{
+  std::atomic<void*> p;
+  p.fetch_add(1); // { dg-error "from here" }
+  p.fetch_sub(1); // { dg-error "from here" }
+  p += 1;	  // { dg-error "from here" }
+  p -= 1;	  // { dg-error "from here" }
+  ++p;		  // { dg-error "from here" }
+  p++;		  // { dg-error "from here" }
+  --p;		  // { dg-error "from here" }
+  p--;		  // { dg-error "from here" }
+}
+
+void
+test02()
+{
+  std::atomic<void(*)()> p;
+  p.fetch_add(1); // { dg-error "from here" }
+  p.fetch_sub(1); // { dg-error "from here" }
+  p += 1;	  // { dg-error "from here" }
+  p -= 1;	  // { dg-error "from here" }
+  ++p;		  // { dg-error "from here" }
+  p++;		  // { dg-error "from here" }
+  --p;		  // { dg-error "from here" }
+  p--;		  // { dg-error "from here" }
+}
+
+void
+test03()
+{
+  volatile std::atomic<void*> p;
+  p.fetch_add(1); // { dg-error "from here" }
+  p.fetch_sub(1); // { dg-error "from here" }
+  p += 1;	  // { dg-error "from here" }
+  p -= 1;	  // { dg-error "from here" }
+  ++p;		  // { dg-error "from here" }
+  p++;		  // { dg-error "from here" }
+  --p;		  // { dg-error "from here" }
+  p--;		  // { dg-error "from here" }
+}
+
+void
+test04()
+{
+  volatile std::atomic<void(*)()> p;
+  p.fetch_add(1); // { dg-error "from here" }
+  p.fetch_sub(1); // { dg-error "from here" }
+  p += 1;	  // { dg-error "from here" }
+  p -= 1;	  // { dg-error "from here" }
+  ++p;		  // { dg-error "from here" }
+  p++;		  // { dg-error "from here" }
+  --p;		  // { dg-error "from here" }
+  p--;		  // { dg-error "from here" }
+}
+
+// { dg-prune-output "static assertion failed" }
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc b/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc
index 1fb4c9c..a0c84bb 100644
--- a/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc
@@ -1,4 +1,4 @@ 
-// { dg-do run { target c++11 } }
+// { dg-do run { target { c++11_only || c++14_only } } }
 // { dg-require-atomic-builtins "" }
 
 // Copyright (C) 2012-2017 Free Software Foundation, Inc.
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/requirements/typedefs.cc b/libstdc++-v3/testsuite/29_atomics/atomic/requirements/typedefs.cc
new file mode 100644
index 0000000..ebbde5e
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic/requirements/typedefs.cc
@@ -0,0 +1,35 @@ 
+// Copyright (C) 2017 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++17" }
+// { dg-do compile { target c++1z } }
+
+#include <atomic>
+
+template<typename T>
+constexpr bool check()
+{
+  typename std::atomic<T>::value_type* pv = (T*)nullptr;
+  typename std::atomic<T>::difference_type* pd = (std::ptrdiff_t*)nullptr;
+  return true;
+}
+
+static_assert( check<int*>(), "" );
+static_assert( check<void*>(), "" );
+static_assert( check<void(*)()>(), "" );
+struct X { };
+static_assert( check<X*>(), "" );
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/requirements/typedefs.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/requirements/typedefs.cc
new file mode 100644
index 0000000..8cf605e
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/requirements/typedefs.cc
@@ -0,0 +1,38 @@ 
+// Copyright (C) 2017 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++17" }
+// { dg-do compile { target c++1z } }
+
+#include <atomic>
+
+template<typename T>
+constexpr bool check()
+{
+  typename std::atomic<T>::value_type* pv = (T*)nullptr;
+  typename std::atomic<T>::difference_type* pd = (T*)nullptr;
+  return true;
+}
+
+static_assert( check<signed short>(), "" );
+static_assert( check<unsigned short>(), "" );
+static_assert( check<signed int>(), "" );
+static_assert( check<unsigned int>(), "" );
+static_assert( check<signed long>(), "" );
+static_assert( check<unsigned long>(), "" );
+static_assert( check<signed long long>(), "" );
+static_assert( check<unsigned long long>(), "" );