[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