[libcxx-commits] [libcxx] aac5b84 - [libc++] Improve atomic_fetch_(add|sub).*.

Mark de Wever via libcxx-commits libcxx-commits at lists.llvm.org
Fri Oct 8 08:42:03 PDT 2021


Author: Mark de Wever
Date: 2021-10-08T17:41:57+02:00
New Revision: aac5b84d4bf7cbb84e7adc491380bf5bfcb8f61b

URL: https://github.com/llvm/llvm-project/commit/aac5b84d4bf7cbb84e7adc491380bf5bfcb8f61b
DIFF: https://github.com/llvm/llvm-project/commit/aac5b84d4bf7cbb84e7adc491380bf5bfcb8f61b.diff

LOG: [libc++] Improve atomic_fetch_(add|sub).*.

While looking at the review comments in D103765 there was an oddity in
the tests for the following functions:
- atomic_fetch_add
- atomic_fetch_add_explicit
- atomic_fetch_sub
- atomic_fetch_sub_explicit

Libc++ allows usage of
`atomic_fetch_add<int>(atomic<int*>*, atomic<int*>::difference_type);`
MSVC and GCC reject this code: https://godbolt.org/z/9d8WzohbE

This makes the atomic `fetch(add|sub).*` Standard conforming and removes the non-conforming extensions.

Fixes PR47908

Reviewed By: ldionne, #libc

Differential Revision: https://reviews.llvm.org/D103983

Added: 
    libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.verify.cpp
    libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.verify.cpp
    libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.verify.cpp
    libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.verify.cpp

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/include/atomic
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.pass.cpp
    libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.pass.cpp

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index da6ede0ad15da..1ec7fa3d20cd0 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -57,3 +57,21 @@ Build System Changes
 - Building the libc++ shared or static library requires a C++ 20 capable compiler.
   Use ``-DLLVM_ENABLE_PROJECTS='clang;compiler-rt' -DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi'``
   to build libc++ using a fresh build of Clang.
+
+- The functions ``std::atomic<T*>::fetch_(add|sub)`` and
+  ``std::atomic_fetch_(add|sub)`` no longer accept a function pointer. While
+  this is technically an API break, the invalid syntax isn't supported by
+  libstc++ and MSVC STL.  See https://godbolt.org/z/49fvzz98d.
+
+- The call of the functions ``std::atomic_(add|sub)(std::atomic<T*>*, ...)``
+  with the explicit template argument ``T`` are now ill-formed. While this is
+  technically an API break, the invalid syntax isn't supported by libstc++ and
+  MSVC STL. See https://godbolt.org/z/v9959re3v.
+
+  Due to this change it's now possible to call these functions with the
+  explicit template argument ``T*``. This allows using the same syntax on the
+  major Standard library implementations.
+  See https://godbolt.org/z/oEfzPhTTb.
+
+  Calls to these functions where the template argument was deduced by the
+  compiler are unaffected by this change.

diff  --git a/libcxx/include/atomic b/libcxx/include/atomic
index 922078c40a347..958935bbd9c06 100644
--- a/libcxx/include/atomic
+++ b/libcxx/include/atomic
@@ -1844,19 +1844,32 @@ struct atomic<_Tp*>
         {__base::store(__d); return __d;}
 
     _LIBCPP_INLINE_VISIBILITY
-    _Tp* fetch_add(ptr
diff _t __op, memory_order __m = memory_order_seq_cst)
-                                                                        volatile _NOEXCEPT
-        {return __cxx_atomic_fetch_add(&this->__a_, __op, __m);}
+    _Tp* fetch_add(ptr
diff _t __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+        // __atomic_fetch_add accepts function pointers, guard against them.
+        static_assert(!is_function<typename remove_pointer<_Tp>::type>::value, "Pointer to function isn't allowed");
+        return __cxx_atomic_fetch_add(&this->__a_, __op, __m);
+    }
+
     _LIBCPP_INLINE_VISIBILITY
-    _Tp* fetch_add(ptr
diff _t __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
-        {return __cxx_atomic_fetch_add(&this->__a_, __op, __m);}
+    _Tp* fetch_add(ptr
diff _t __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+        // __atomic_fetch_add accepts function pointers, guard against them.
+        static_assert(!is_function<typename remove_pointer<_Tp>::type>::value, "Pointer to function isn't allowed");
+        return __cxx_atomic_fetch_add(&this->__a_, __op, __m);
+    }
+
     _LIBCPP_INLINE_VISIBILITY
-    _Tp* fetch_sub(ptr
diff _t __op, memory_order __m = memory_order_seq_cst)
-                                                                        volatile _NOEXCEPT
-        {return __cxx_atomic_fetch_sub(&this->__a_, __op, __m);}
+    _Tp* fetch_sub(ptr
diff _t __op, memory_order __m = memory_order_seq_cst) volatile _NOEXCEPT {
+        // __atomic_fetch_add accepts function pointers, guard against them.
+        static_assert(!is_function<typename remove_pointer<_Tp>::type>::value, "Pointer to function isn't allowed");
+        return __cxx_atomic_fetch_sub(&this->__a_, __op, __m);
+    }
+
     _LIBCPP_INLINE_VISIBILITY
-    _Tp* fetch_sub(ptr
diff _t __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
-        {return __cxx_atomic_fetch_sub(&this->__a_, __op, __m);}
+    _Tp* fetch_sub(ptr
diff _t __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT {
+        // __atomic_fetch_add accepts function pointers, guard against them.
+        static_assert(!is_function<typename remove_pointer<_Tp>::type>::value, "Pointer to function isn't allowed");
+        return __cxx_atomic_fetch_sub(&this->__a_, __op, __m);
+    }
 
     _LIBCPP_INLINE_VISIBILITY
     _Tp* operator++(int) volatile _NOEXCEPT            {return fetch_add(1);}
@@ -2192,11 +2205,7 @@ void atomic_notify_all(atomic<_Tp>* __o) _NOEXCEPT
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
+_Tp
 atomic_fetch_add(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op) _NOEXCEPT
 {
     return __o->fetch_add(__op);
@@ -2204,70 +2213,24 @@ atomic_fetch_add(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_typ
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
+_Tp
 atomic_fetch_add(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op) _NOEXCEPT
 {
     return __o->fetch_add(__op);
 }
 
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_add(volatile atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op) _NOEXCEPT
-{
-    return __o->fetch_add(__op);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_add(atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op) _NOEXCEPT
-{
-    return __o->fetch_add(__op);
-}
-
 // atomic_fetch_add_explicit
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
-atomic_fetch_add_explicit(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
-{
-    return __o->fetch_add(__op, __m);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
-atomic_fetch_add_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
+_Tp atomic_fetch_add_explicit(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
 {
     return __o->fetch_add(__op, __m);
 }
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_add_explicit(volatile atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op, memory_order __m) _NOEXCEPT
-{
-    return __o->fetch_add(__op, __m);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_add_explicit(atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op, memory_order __m) _NOEXCEPT
+_Tp atomic_fetch_add_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
 {
     return __o->fetch_add(__op, __m);
 }
@@ -2276,40 +2239,14 @@ atomic_fetch_add_explicit(atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_t
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
-atomic_fetch_sub(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op) _NOEXCEPT
-{
-    return __o->fetch_sub(__op);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
-atomic_fetch_sub(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op) _NOEXCEPT
-{
-    return __o->fetch_sub(__op);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_sub(volatile atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op) _NOEXCEPT
+_Tp atomic_fetch_sub(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op) _NOEXCEPT
 {
     return __o->fetch_sub(__op);
 }
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_sub(atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op) _NOEXCEPT
+_Tp atomic_fetch_sub(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op) _NOEXCEPT
 {
     return __o->fetch_sub(__op);
 }
@@ -2318,40 +2255,14 @@ atomic_fetch_sub(atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op)
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
-atomic_fetch_sub_explicit(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
-{
-    return __o->fetch_sub(__op, __m);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-typename enable_if
-<
-    is_integral<_Tp>::value && !is_same<_Tp, bool>::value && !is_const<_Tp>::value,
-    _Tp
->::type
-atomic_fetch_sub_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
-{
-    return __o->fetch_sub(__op, __m);
-}
-
-template <class _Tp>
-_LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_sub_explicit(volatile atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op, memory_order __m) _NOEXCEPT
+_Tp atomic_fetch_sub_explicit(volatile atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
 {
     return __o->fetch_sub(__op, __m);
 }
 
 template <class _Tp>
 _LIBCPP_INLINE_VISIBILITY
-_Tp*
-atomic_fetch_sub_explicit(atomic<_Tp*>* __o, typename atomic<_Tp*>::
diff erence_type __op, memory_order __m) _NOEXCEPT
+_Tp atomic_fetch_sub_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::
diff erence_type __op, memory_order __m) _NOEXCEPT
 {
     return __o->fetch_sub(__op, __m);
 }

diff  --git a/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.verify.cpp b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.verify.cpp
new file mode 100644
index 0000000000000..f3454b990edc6
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.verify.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-has-no-threads
+
+// <atomic>
+
+// template <class T>
+//     T* atomic_fetch_add(volatile atomic<T*>* obj, ptr
diff _t op)
+// template <class T>
+//     T* atomic_fetch_add(atomic<T*>* obj, ptr
diff _t op);
+
+#include <atomic>
+
+void void_pointer() {
+  {
+    volatile std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_add(&obj, 0);
+  }
+  {
+    std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_add(&obj, 0);
+  }
+}
+
+struct Incomplete;
+
+void pointer_to_incomplete_type() {
+  {
+    volatile std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_add(&obj, 0);
+  }
+  {
+    std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_add(&obj, 0);
+  }
+}
+
+void function_pointer() {
+  {
+    volatile std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_add(&fun, 0);
+  }
+  {
+    std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_add(&fun, 0);
+  }
+}
+
+struct S {
+  void fun(int);
+};
+
+void member_function_pointer() {
+  {
+    volatile std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_add' in}}
+    std::atomic_fetch_add(&fun, 0);
+  }
+  {
+    std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_add' in}}
+    std::atomic_fetch_add(&fun, 0);
+  }
+}

diff  --git a/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.verify.cpp b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.verify.cpp
new file mode 100644
index 0000000000000..44fb219f7fcc5
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.verify.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-has-no-threads
+
+// <atomic>
+
+// template <class T>
+//     T*
+//     atomic_fetch_add_explicit(volatile atomic<T*>* obj, ptr
diff _t op,
+//                               memory_order m);
+// template <class T>
+//     T*
+//     atomic_fetch_add_explicit(atomic<T*>* obj, ptr
diff _t op, memory_order m);
+
+#include <atomic>
+
+void void_pointer() {
+  {
+    volatile std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_add_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_add_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+}
+
+struct Incomplete;
+
+void pointer_to_incomplete_type() {
+  {
+    volatile std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_add_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_add_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+}
+
+void function_pointer() {
+  {
+    volatile std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_add_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_add_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+}
+
+struct S {
+  void fun(int);
+};
+
+void member_function_pointer() {
+  {
+    volatile std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_add' in}}
+    std::atomic_fetch_add_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_add' in}}
+    std::atomic_fetch_add_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+}

diff  --git a/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.verify.cpp b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.verify.cpp
new file mode 100644
index 0000000000000..0ecda4d3a2fbb
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.verify.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-has-no-threads
+
+// <atomic>
+
+// template <class T>
+//     T* atomic_fetch_sub(volatile atomic<T*>* obj, ptr
diff _t op)
+// template <class T>
+//     T* atomic_fetch_sub(atomic<T*>* obj, ptr
diff _t op);
+
+#include <atomic>
+
+void void_pointer() {
+  {
+    volatile std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_sub(&obj, 0);
+  }
+  {
+    std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_sub(&obj, 0);
+  }
+}
+
+struct Incomplete;
+
+void pointer_to_incomplete_type() {
+  {
+    volatile std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_sub(&obj, 0);
+  }
+  {
+    std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_sub(&obj, 0);
+  }
+}
+
+void function_pointer() {
+  {
+    volatile std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_sub(&fun, 0);
+  }
+  {
+    std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_sub(&fun, 0);
+  }
+}
+
+struct S {
+  void fun(int);
+};
+
+void member_function_pointer() {
+  {
+    volatile std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_sub' in}}
+    std::atomic_fetch_sub(&fun, 0);
+  }
+  {
+    std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_sub' in}}
+    std::atomic_fetch_sub(&fun, 0);
+  }
+}

diff  --git a/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.verify.cpp b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.verify.cpp
new file mode 100644
index 0000000000000..be1e6fdd4036a
--- /dev/null
+++ b/libcxx/test/libcxx/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.verify.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: libcpp-has-no-threads
+
+// <atomic>
+
+// template <class T>
+//     T*
+//     atomic_fetch_sub_explicit(volatile atomic<T*>* obj, ptr
diff _t op,
+//                               memory_order m);
+// template <class T>
+//     T*
+//     atomic_fetch_sub_explicit(atomic<T*>* obj, ptr
diff _t op, memory_order m);
+
+#include <atomic>
+
+void void_pointer() {
+  {
+    volatile std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_sub_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<void*> obj;
+    // expected-error at atomic:* {{incomplete type 'void' where a complete type is required}}
+    std::atomic_fetch_sub_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+}
+
+struct Incomplete;
+
+void pointer_to_incomplete_type() {
+  {
+    volatile std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_sub_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<Incomplete*> obj;
+    // expected-error at atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
+    std::atomic_fetch_sub_explicit(&obj, 0, std::memory_order_relaxed);
+  }
+}
+
+void function_pointer() {
+  {
+    volatile std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_sub_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<void (*)(int)> fun;
+    // expected-error at atomic:* {{static_assert failed due to requirement '!is_function<void (int)>::value' "Pointer to function isn't allowed"}}
+    std::atomic_fetch_sub_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+}
+
+struct S {
+  void fun(int);
+};
+
+void member_function_pointer() {
+  {
+    volatile std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_sub' in}}
+    std::atomic_fetch_sub_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+  {
+    std::atomic<void (S::*)(int)> fun;
+    // expected-error at atomic:* {{no member named 'fetch_sub' in}}
+    std::atomic_fetch_sub_explicit(&fun, 0, std::memory_order_relaxed);
+  }
+}

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.pass.cpp
index f48892b1e9733..4863f2a4bcc96 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add.pass.cpp
@@ -20,11 +20,11 @@
 //
 // template <class T>
 //     T*
-//     atomic_fetch_add(volatile atomic<T*>* obj, ptr
diff _t op);
+//     atomic_fetch_add(volatile atomic<T>* obj, typename atomic<T>::
diff erence_type) noexcept;
 //
 // template <class T>
 //     T*
-//     atomic_fetch_add(atomic<T*>* obj, ptr
diff _t op);
+//     atomic_fetch_add(atomic<T*>* obj, typename atomic<T>::
diff erence_type) noexcept;
 
 #include <atomic>
 #include <type_traits>
@@ -41,12 +41,14 @@ struct TestFn {
         A t(T(1));
         assert(std::atomic_fetch_add(&t, T(2)) == T(1));
         assert(t == T(3));
+        ASSERT_NOEXCEPT(std::atomic_fetch_add(&t, 0));
     }
     {
         typedef std::atomic<T> A;
         volatile A t(T(1));
         assert(std::atomic_fetch_add(&t, T(2)) == T(1));
         assert(t == T(3));
+        ASSERT_NOEXCEPT(std::atomic_fetch_add(&t, 0));
     }
   }
 };
@@ -57,26 +59,22 @@ void testp()
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        A t(T(1 * sizeof(X)));
-        assert(std::atomic_fetch_add(&t, 2) == T(1*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is nonconforming
-        std::atomic_fetch_add<X>(&t, 0);
-#else
+        X a[3] = {0};
+        A t(&a[0]);
+        assert(std::atomic_fetch_add(&t, 2) == &a[0]);
         std::atomic_fetch_add<T>(&t, 0);
-#endif // _LIBCPP_VERSION
-        assert(t == T(3*sizeof(X)));
+        assert(t == &a[2]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_add(&t, 0));
     }
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        volatile A t(T(1 * sizeof(X)));
-        assert(std::atomic_fetch_add(&t, 2) == T(1*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is nonconforming
-        std::atomic_fetch_add<X>(&t, 0);
-#else
+        X a[3] = {0};
+        volatile A t(&a[0]);
+        assert(std::atomic_fetch_add(&t, 2) == &a[0]);
         std::atomic_fetch_add<T>(&t, 0);
-#endif // _LIBCPP_VERSION
-        assert(t == T(3*sizeof(X)));
+        assert(t == &a[2]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_add(&t, 0));
     }
 }
 

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.pass.cpp
index ca41e7bc08917..092666a171949 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_add_explicit.pass.cpp
@@ -42,6 +42,7 @@ struct TestFn {
         assert(std::atomic_fetch_add_explicit(&t, T(2),
                                             std::memory_order_seq_cst) == T(1));
         assert(t == T(3));
+        ASSERT_NOEXCEPT(std::atomic_fetch_add_explicit(&t, 0, std::memory_order_relaxed));
     }
     {
         typedef std::atomic<T> A;
@@ -49,6 +50,7 @@ struct TestFn {
         assert(std::atomic_fetch_add_explicit(&t, T(2),
                                             std::memory_order_seq_cst) == T(1));
         assert(t == T(3));
+        ASSERT_NOEXCEPT(std::atomic_fetch_add_explicit(&t, 0, std::memory_order_relaxed));
     }
   }
 };
@@ -60,28 +62,22 @@ testp()
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        A t(T(1 * sizeof(X)));
-        assert(std::atomic_fetch_add_explicit(&t, 2,
-                                  std::memory_order_seq_cst) == T(1*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is not conforming
-        std::atomic_fetch_add_explicit<X>(&t, 0, std::memory_order_relaxed);
-#else
+        X a[3] = {0};
+        A t(&a[0]);
+        assert(std::atomic_fetch_add_explicit(&t, 2, std::memory_order_seq_cst) == &a[0]);
         std::atomic_fetch_add_explicit<T>(&t, 0, std::memory_order_relaxed);
-#endif // _LIBCPP_VERSION
-        assert(t == T(3*sizeof(X)));
+        assert(t == &a[2]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_add_explicit(&t, 0, std::memory_order_relaxed));
     }
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        volatile A t(T(1 * sizeof(X)));
-        assert(std::atomic_fetch_add_explicit(&t, 2,
-                                  std::memory_order_seq_cst) == T(1*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is not conforming
-        std::atomic_fetch_add_explicit<X>(&t, 0, std::memory_order_relaxed);
-#else
+        X a[3] = {0};
+        volatile A t(&a[0]);
+        assert(std::atomic_fetch_add_explicit(&t, 2, std::memory_order_seq_cst) == &a[0]);
         std::atomic_fetch_add_explicit<T>(&t, 0, std::memory_order_relaxed);
-#endif // _LIBCPP_VERSION
-        assert(t == T(3*sizeof(X)));
+        assert(t == &a[2]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_add_explicit(&t, 0, std::memory_order_relaxed));
     }
 }
 

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.pass.cpp
index d065c46d79198..deba7816e5b41 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub.pass.cpp
@@ -41,12 +41,14 @@ struct TestFn {
         A t(T(3));
         assert(std::atomic_fetch_sub(&t, T(2)) == T(3));
         assert(t == T(1));
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub(&t, 0));
     }
     {
         typedef std::atomic<T> A;
         volatile A t(T(3));
         assert(std::atomic_fetch_sub(&t, T(2)) == T(3));
         assert(t == T(1));
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub(&t, 0));
     }
   }
 };
@@ -57,26 +59,22 @@ void testp()
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        A t(T(3 * sizeof(X)));
-        assert(std::atomic_fetch_sub(&t, 2) == T(3*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is nonconforming
-        std::atomic_fetch_sub<X>(&t, 0);
-#else
+        X a[3] = {0};
+        A t(&a[2]);
+        assert(std::atomic_fetch_sub(&t, 2) == &a[2]);
         std::atomic_fetch_sub<T>(&t, 0);
-#endif // _LIBCPP_VERSION
-        assert(t == T(1*sizeof(X)));
+        assert(t == &a[0]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub(&t, 0));
     }
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        volatile A t(T(3 * sizeof(X)));
-        assert(std::atomic_fetch_sub(&t, 2) == T(3*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is nonconforming
-        std::atomic_fetch_sub<X>(&t, 0);
-#else
+        X a[3] = {0};
+        volatile A t(&a[2]);
+        assert(std::atomic_fetch_sub(&t, 2) == &a[2]);
         std::atomic_fetch_sub<T>(&t, 0);
-#endif // _LIBCPP_VERSION
-        assert(t == T(1*sizeof(X)));
+        assert(t == &a[0]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub(&t, 0));
     }
 }
 

diff  --git a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.pass.cpp b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.pass.cpp
index 62fc4b9a12d5d..919c389b2d074 100644
--- a/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.pass.cpp
+++ b/libcxx/test/std/atomics/atomics.types.operations/atomics.types.operations.req/atomic_fetch_sub_explicit.pass.cpp
@@ -43,6 +43,7 @@ struct TestFn {
         assert(std::atomic_fetch_sub_explicit(&t, T(2),
                                             std::memory_order_seq_cst) == T(3));
         assert(t == T(1));
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub_explicit(&t, 0, std::memory_order_relaxed));
     }
     {
         typedef std::atomic<T> A;
@@ -50,6 +51,7 @@ struct TestFn {
         assert(std::atomic_fetch_sub_explicit(&t, T(2),
                                             std::memory_order_seq_cst) == T(3));
         assert(t == T(1));
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub_explicit(&t, 0, std::memory_order_relaxed));
     }
   }
 };
@@ -60,28 +62,22 @@ void testp()
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        A t(T(3 * sizeof(X)));
-        assert(std::atomic_fetch_sub_explicit(&t, 2,
-                                  std::memory_order_seq_cst) == T(3*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is nonconforming
-        std::atomic_fetch_sub_explicit<X>(&t, 0, std::memory_order_relaxed);
-#else
+        X a[3] = {0};
+        A t(&a[2]);
+        assert(std::atomic_fetch_sub_explicit(&t, 2, std::memory_order_seq_cst) == &a[2]);
         std::atomic_fetch_sub_explicit<T>(&t, 0, std::memory_order_relaxed);
-#endif // _LIBCPP_VERSION
-        assert(t == T(1*sizeof(X)));
+        assert(t == &a[0]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub_explicit(&t, 0, std::memory_order_relaxed));
     }
     {
         typedef std::atomic<T> A;
         typedef typename std::remove_pointer<T>::type X;
-        volatile A t(T(3 * sizeof(X)));
-        assert(std::atomic_fetch_sub_explicit(&t, 2,
-                                  std::memory_order_seq_cst) == T(3*sizeof(X)));
-#ifdef _LIBCPP_VERSION // libc++ is nonconforming
-        std::atomic_fetch_sub_explicit<X>(&t, 0, std::memory_order_relaxed);
-#else
+        X a[3] = {0};
+        volatile A t(&a[2]);
+        assert(std::atomic_fetch_sub_explicit(&t, 2, std::memory_order_seq_cst) == &a[2]);
         std::atomic_fetch_sub_explicit<T>(&t, 0, std::memory_order_relaxed);
-#endif // _LIBCPP_VERSION
-        assert(t == T(1*sizeof(X)));
+        assert(t == &a[0]);
+        ASSERT_NOEXCEPT(std::atomic_fetch_sub_explicit(&t, 0, std::memory_order_relaxed));
     }
 }
 


        


More information about the libcxx-commits mailing list