mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 06:36:07 +00:00
[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
This commit is contained in:
parent
b2ee408dde
commit
aac5b84d4b
@ -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.
|
||||
|
@ -1844,19 +1844,32 @@ struct atomic<_Tp*>
|
||||
{__base::store(__d); return __d;}
|
||||
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
_Tp* fetch_add(ptrdiff_t __op, memory_order __m = memory_order_seq_cst)
|
||||
volatile _NOEXCEPT
|
||||
{return __cxx_atomic_fetch_add(&this->__a_, __op, __m);}
|
||||
_Tp* fetch_add(ptrdiff_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(ptrdiff_t __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
|
||||
{return __cxx_atomic_fetch_add(&this->__a_, __op, __m);}
|
||||
_Tp* fetch_add(ptrdiff_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(ptrdiff_t __op, memory_order __m = memory_order_seq_cst)
|
||||
volatile _NOEXCEPT
|
||||
{return __cxx_atomic_fetch_sub(&this->__a_, __op, __m);}
|
||||
_Tp* fetch_sub(ptrdiff_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(ptrdiff_t __op, memory_order __m = memory_order_seq_cst) _NOEXCEPT
|
||||
{return __cxx_atomic_fetch_sub(&this->__a_, __op, __m);}
|
||||
_Tp* fetch_sub(ptrdiff_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>::difference_type __op) _NOEXCEPT
|
||||
{
|
||||
return __o->fetch_add(__op);
|
||||
@ -2204,70 +2213,24 @@ atomic_fetch_add(volatile atomic<_Tp>* __o, typename atomic<_Tp>::difference_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>::difference_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*>::difference_type __op) _NOEXCEPT
|
||||
{
|
||||
return __o->fetch_add(__op);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
_Tp*
|
||||
atomic_fetch_add(atomic<_Tp*>* __o, typename atomic<_Tp*>::difference_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>::difference_type __op, memory_order __m) _NOEXCEPT
|
||||
_Tp atomic_fetch_add_explicit(volatile atomic<_Tp>* __o, typename atomic<_Tp>::difference_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>::difference_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*>::difference_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*>::difference_type __op, memory_order __m) _NOEXCEPT
|
||||
_Tp atomic_fetch_add_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::difference_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*>::difference_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>::difference_type __op) _NOEXCEPT
|
||||
_Tp atomic_fetch_sub(volatile atomic<_Tp>* __o, typename atomic<_Tp>::difference_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>::difference_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*>::difference_type __op) _NOEXCEPT
|
||||
{
|
||||
return __o->fetch_sub(__op);
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_INLINE_VISIBILITY
|
||||
_Tp*
|
||||
atomic_fetch_sub(atomic<_Tp*>* __o, typename atomic<_Tp*>::difference_type __op) _NOEXCEPT
|
||||
_Tp atomic_fetch_sub(atomic<_Tp>* __o, typename atomic<_Tp>::difference_type __op) _NOEXCEPT
|
||||
{
|
||||
return __o->fetch_sub(__op);
|
||||
}
|
||||
@ -2318,40 +2255,14 @@ atomic_fetch_sub(atomic<_Tp*>* __o, typename atomic<_Tp*>::difference_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>::difference_type __op, memory_order __m) _NOEXCEPT
|
||||
_Tp atomic_fetch_sub_explicit(volatile atomic<_Tp>* __o, typename atomic<_Tp>::difference_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>::difference_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*>::difference_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*>::difference_type __op, memory_order __m) _NOEXCEPT
|
||||
_Tp atomic_fetch_sub_explicit(atomic<_Tp>* __o, typename atomic<_Tp>::difference_type __op, memory_order __m) _NOEXCEPT
|
||||
{
|
||||
return __o->fetch_sub(__op, __m);
|
||||
}
|
||||
|
@ -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, ptrdiff_t op)
|
||||
// template <class T>
|
||||
// T* atomic_fetch_add(atomic<T*>* obj, ptrdiff_t op);
|
||||
|
||||
#include <atomic>
|
||||
|
||||
void void_pointer() {
|
||||
{
|
||||
volatile std::atomic<void*> obj;
|
||||
// expected-error@atomic:* {{incomplete type 'void' where a complete type is required}}
|
||||
std::atomic_fetch_add(&obj, 0);
|
||||
}
|
||||
{
|
||||
std::atomic<void*> obj;
|
||||
// expected-error@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@atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
|
||||
std::atomic_fetch_add(&obj, 0);
|
||||
}
|
||||
{
|
||||
std::atomic<Incomplete*> obj;
|
||||
// expected-error@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@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@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@atomic:* {{no member named 'fetch_add' in}}
|
||||
std::atomic_fetch_add(&fun, 0);
|
||||
}
|
||||
{
|
||||
std::atomic<void (S::*)(int)> fun;
|
||||
// expected-error@atomic:* {{no member named 'fetch_add' in}}
|
||||
std::atomic_fetch_add(&fun, 0);
|
||||
}
|
||||
}
|
@ -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, ptrdiff_t op,
|
||||
// memory_order m);
|
||||
// template <class T>
|
||||
// T*
|
||||
// atomic_fetch_add_explicit(atomic<T*>* obj, ptrdiff_t op, memory_order m);
|
||||
|
||||
#include <atomic>
|
||||
|
||||
void void_pointer() {
|
||||
{
|
||||
volatile std::atomic<void*> obj;
|
||||
// expected-error@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@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@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@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@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@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@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@atomic:* {{no member named 'fetch_add' in}}
|
||||
std::atomic_fetch_add_explicit(&fun, 0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
@ -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, ptrdiff_t op)
|
||||
// template <class T>
|
||||
// T* atomic_fetch_sub(atomic<T*>* obj, ptrdiff_t op);
|
||||
|
||||
#include <atomic>
|
||||
|
||||
void void_pointer() {
|
||||
{
|
||||
volatile std::atomic<void*> obj;
|
||||
// expected-error@atomic:* {{incomplete type 'void' where a complete type is required}}
|
||||
std::atomic_fetch_sub(&obj, 0);
|
||||
}
|
||||
{
|
||||
std::atomic<void*> obj;
|
||||
// expected-error@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@atomic:* {{incomplete type 'Incomplete' where a complete type is required}}
|
||||
std::atomic_fetch_sub(&obj, 0);
|
||||
}
|
||||
{
|
||||
std::atomic<Incomplete*> obj;
|
||||
// expected-error@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@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@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@atomic:* {{no member named 'fetch_sub' in}}
|
||||
std::atomic_fetch_sub(&fun, 0);
|
||||
}
|
||||
{
|
||||
std::atomic<void (S::*)(int)> fun;
|
||||
// expected-error@atomic:* {{no member named 'fetch_sub' in}}
|
||||
std::atomic_fetch_sub(&fun, 0);
|
||||
}
|
||||
}
|
@ -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, ptrdiff_t op,
|
||||
// memory_order m);
|
||||
// template <class T>
|
||||
// T*
|
||||
// atomic_fetch_sub_explicit(atomic<T*>* obj, ptrdiff_t op, memory_order m);
|
||||
|
||||
#include <atomic>
|
||||
|
||||
void void_pointer() {
|
||||
{
|
||||
volatile std::atomic<void*> obj;
|
||||
// expected-error@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@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@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@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@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@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@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@atomic:* {{no member named 'fetch_sub' in}}
|
||||
std::atomic_fetch_sub_explicit(&fun, 0, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
@ -20,11 +20,11 @@
|
||||
//
|
||||
// template <class T>
|
||||
// T*
|
||||
// atomic_fetch_add(volatile atomic<T*>* obj, ptrdiff_t op);
|
||||
// atomic_fetch_add(volatile atomic<T>* obj, typename atomic<T>::difference_type) noexcept;
|
||||
//
|
||||
// template <class T>
|
||||
// T*
|
||||
// atomic_fetch_add(atomic<T*>* obj, ptrdiff_t op);
|
||||
// atomic_fetch_add(atomic<T*>* obj, typename atomic<T>::difference_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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user