Skip to content

Commit

Permalink
Add member difference_type to atomic<void*> and its friends
Browse files Browse the repository at this point in the history
It seems intentional that `atomic(_ref)<T*>` are SFINAE-friendly on
atomic pointer arithmetic. So this PR adds another mediate base class
to avoid breaking the SFINAE-friendliness.

Also updates the comments in
 `Dev11_0863628_atomic_compare_exchange/test.cpp` to cite WG21-N4981.
  • Loading branch information
frederick-vs-ja committed May 23, 2024
1 parent 63354c3 commit cf53dba
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 23 deletions.
14 changes: 12 additions & 2 deletions stl/inc/atomic
Original file line number Diff line number Diff line change
Expand Up @@ -2122,14 +2122,24 @@ struct _Atomic_pointer<_Ty&> : _Atomic_storage<_Ty&> {
}
};

template <class _Ty>
struct _Atomic_nonobject_pointer : _Atomic_storage<_Ty> {
using _Base = _Atomic_storage<_Ty>;
using difference_type = ptrdiff_t;

using _Base::_Base;
};

#define ATOMIC_VAR_INIT(_Value) \
{ _Value }

template <class _TVal, class _Ty = _TVal>
using _Choose_atomic_base2_t =
typename _Select<is_integral_v<_TVal> && !is_same_v<bool, _TVal>>::template _Apply<_Atomic_integral_facade<_Ty>,
typename _Select<is_pointer_v<_TVal> && is_object_v<remove_pointer_t<_TVal>>>::template _Apply<
_Atomic_pointer<_Ty>, _Atomic_storage<_Ty>>>;
typename _Select<is_pointer_v<_TVal>>::template _Apply<
typename _Select<is_object_v<remove_pointer_t<_TVal>>>::template _Apply<_Atomic_pointer<_Ty>,
_Atomic_nonobject_pointer<_Ty>>,
_Atomic_storage<_Ty>>>;

#if _HAS_CXX20
template <class _TVal, class _Ty = _TVal>
Expand Down
85 changes: 77 additions & 8 deletions tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <cstring>
Expand All @@ -20,14 +21,18 @@ using namespace std;

#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)

// N3797 29.6.5 [atomics.types.operations.req]/21:
// bool A::compare_exchange_weak(C & expected, C desired, memory_order order = memory_order_seq_cst) volatile noexcept;
// bool A::compare_exchange_weak(C & expected, C desired, memory_order order = memory_order_seq_cst) noexcept;
// bool A::compare_exchange_strong(C & expected, C desired, memory_order order = memory_order_seq_cst) volatile
// noexcept; bool A::compare_exchange_strong(C & expected, C desired, memory_order order = memory_order_seq_cst)
// noexcept; When only one memory_order argument is supplied, the value of success is order, and the value of failure is
// order except that a value of memory_order_acq_rel shall be replaced by the value memory_order_acquire and a value of
// memory_order_release shall be replaced by the value memory_order_relaxed.
// N4981 [atomics.types.operations]/23:
// bool compare_exchange_weak(T& expected, T desired,
// memory_order order = memory_order::seq_cst) volatile noexcept;
// bool compare_exchange_weak(T& expected, T desired,
// memory_order order = memory_order::seq_cst) noexcept;
// bool compare_exchange_strong(T& expected, T desired,
// memory_order order = memory_order::seq_cst) volatile noexcept;
// bool compare_exchange_strong(T& expected, T desired,
// memory_order order = memory_order::seq_cst) noexcept;
// When only one memory_order argument is supplied, the value of success is order, and the value of failure is order
// except that a value of memory_order::acq_rel shall be replaced by the value memory_order::acquire and a value of
// memory_order::release shall be replaced by the value memory_order::relaxed.

template <typename T>
void test(T t) {
Expand Down Expand Up @@ -553,6 +558,70 @@ void test_double_identical_results() {
#endif // _HAS_CXX20
}

// Also test GH-4688 "<atomic>: atomic_ref<void*> and atomic<void*> lack difference_type"
template <class, class = void>
constexpr bool atomic_has_member_difference_type = false;
template <class T>
constexpr bool atomic_has_member_difference_type<T, void_t<typename atomic<T>::difference_type>> = true;

STATIC_ASSERT(
atomic_has_member_difference_type<signed char>&& is_same_v<atomic<signed char>::difference_type, signed char>);
STATIC_ASSERT(atomic_has_member_difference_type<short>&& is_same_v<atomic<short>::difference_type, short>);
STATIC_ASSERT(atomic_has_member_difference_type<int>&& is_same_v<atomic<int>::difference_type, int>);
STATIC_ASSERT(atomic_has_member_difference_type<long>&& is_same_v<atomic<long>::difference_type, long>);
STATIC_ASSERT(atomic_has_member_difference_type<long long>&& is_same_v<atomic<long long>::difference_type, long long>);
STATIC_ASSERT(atomic_has_member_difference_type<unsigned char>&&
is_same_v<atomic<unsigned char>::difference_type, unsigned char>);
STATIC_ASSERT(atomic_has_member_difference_type<unsigned short>&&
is_same_v<atomic<unsigned short>::difference_type, unsigned short>);
STATIC_ASSERT(
atomic_has_member_difference_type<unsigned int>&& is_same_v<atomic<unsigned int>::difference_type, unsigned int>);
STATIC_ASSERT(atomic_has_member_difference_type<unsigned long>&&
is_same_v<atomic<unsigned long>::difference_type, unsigned long>);
STATIC_ASSERT(atomic_has_member_difference_type<unsigned long long>&&
is_same_v<atomic<unsigned long long>::difference_type, unsigned long long>);
STATIC_ASSERT(atomic_has_member_difference_type<char>&& is_same_v<atomic<char>::difference_type, char>);
#ifdef __cpp_char8_t
STATIC_ASSERT(atomic_has_member_difference_type<char8_t>&& is_same_v<atomic<char8_t>::difference_type, char8_t>);
#endif // defined(__cpp_char8_t)
STATIC_ASSERT(atomic_has_member_difference_type<char16_t>&& is_same_v<atomic<char16_t>::difference_type, char16_t>);
STATIC_ASSERT(atomic_has_member_difference_type<char32_t>&& is_same_v<atomic<char32_t>::difference_type, char32_t>);
STATIC_ASSERT(atomic_has_member_difference_type<wchar_t>&& is_same_v<atomic<wchar_t>::difference_type, wchar_t>);

#if _HAS_CXX20 // P0020R6 Floating Point Atomic
STATIC_ASSERT(atomic_has_member_difference_type<float>&& is_same_v<atomic<float>::difference_type, float>);
STATIC_ASSERT(atomic_has_member_difference_type<double>&& is_same_v<atomic<double>::difference_type, double>);
STATIC_ASSERT(
atomic_has_member_difference_type<long double>&& is_same_v<atomic<long double>::difference_type, long double>);
#else // _HAS_CXX20 / !_HAS_CXX20
STATIC_ASSERT(!atomic_has_member_difference_type<float>);
STATIC_ASSERT(!atomic_has_member_difference_type<double>);
STATIC_ASSERT(!atomic_has_member_difference_type<long double>);
#endif // ^^^ !_HAS_CXX20 ^^^

STATIC_ASSERT(atomic_has_member_difference_type<int*>&& is_same_v<atomic<int*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(atomic_has_member_difference_type<bool*>&& is_same_v<atomic<bool*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(
atomic_has_member_difference_type<const int*>&& is_same_v<atomic<const int*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(
atomic_has_member_difference_type<volatile bool*>&& is_same_v<atomic<volatile bool*>::difference_type, ptrdiff_t>);

STATIC_ASSERT(atomic_has_member_difference_type<void*>&& is_same_v<atomic<void*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(
atomic_has_member_difference_type<const void*>&& is_same_v<atomic<const void*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(
atomic_has_member_difference_type<volatile void*>&& is_same_v<atomic<volatile void*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(atomic_has_member_difference_type<const volatile void*>&&
is_same_v<atomic<const volatile void*>::difference_type, ptrdiff_t>);
STATIC_ASSERT(
atomic_has_member_difference_type<void (*)()>&& is_same_v<atomic<void (*)()>::difference_type, ptrdiff_t>);

STATIC_ASSERT(!atomic_has_member_difference_type<bool>);
STATIC_ASSERT(!atomic_has_member_difference_type<nullptr_t>);
STATIC_ASSERT(!atomic_has_member_difference_type<Bytes<4>>);
STATIC_ASSERT(!atomic_has_member_difference_type<Bytes<8>>);
STATIC_ASSERT(!atomic_has_member_difference_type<Bytes<12>>);

int main() {
X x = {1729};
test(x);
Expand Down
101 changes: 88 additions & 13 deletions tests/std/tests/P0019R8_atomic_ref/test.cpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#if defined(TEST_CMPXCHG16B) && (defined(__clang__) || !defined(_M_X64))
// Skip Clang because it would require the -mcx16 compiler option.
// Skip non-x64 because _STD_ATOMIC_ALWAYS_USE_CMPXCHG16B is always 1 for ARM64, and is forbidden to be 1 for 32-bit.
int main() {}
#else // ^^^ skip test / run test vvv

#include <atomic>
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <execution>
#include <functional>
#include <memory>
#include <numeric>
#include <type_traits>
#include <vector>

struct bigint {
int value;
Expand All @@ -40,6 +27,94 @@ struct int128 {
}
};

// Also test GH-4688 "<atomic>: atomic_ref<void*> and atomic<void*> lack difference_type"
template <class T>
constexpr bool atomic_ref_has_member_difference_type = requires { typename std::atomic_ref<T>::difference_type; };

static_assert(atomic_ref_has_member_difference_type<signed char>
&& std::is_same_v<std::atomic_ref<signed char>::difference_type, signed char>);
static_assert(
atomic_ref_has_member_difference_type<short> && std::is_same_v<std::atomic_ref<short>::difference_type, short>);
static_assert(atomic_ref_has_member_difference_type<int> && std::is_same_v<std::atomic_ref<int>::difference_type, int>);
static_assert(
atomic_ref_has_member_difference_type<long> && std::is_same_v<std::atomic_ref<long>::difference_type, long>);
static_assert(atomic_ref_has_member_difference_type<long long>
&& std::is_same_v<std::atomic_ref<long long>::difference_type, long long>);
static_assert(atomic_ref_has_member_difference_type<unsigned char>
&& std::is_same_v<std::atomic_ref<unsigned char>::difference_type, unsigned char>);
static_assert(atomic_ref_has_member_difference_type<unsigned short>
&& std::is_same_v<std::atomic_ref<unsigned short>::difference_type, unsigned short>);
static_assert(atomic_ref_has_member_difference_type<unsigned int>
&& std::is_same_v<std::atomic_ref<unsigned int>::difference_type, unsigned int>);
static_assert(atomic_ref_has_member_difference_type<unsigned long>
&& std::is_same_v<std::atomic_ref<unsigned long>::difference_type, unsigned long>);
static_assert(atomic_ref_has_member_difference_type<unsigned long long>
&& std::is_same_v<std::atomic_ref<unsigned long long>::difference_type, unsigned long long>);
static_assert(
atomic_ref_has_member_difference_type<char> && std::is_same_v<std::atomic_ref<char>::difference_type, char>);
#ifdef __cpp_char8_t
static_assert(atomic_ref_has_member_difference_type<char8_t>
&& std::is_same_v<std::atomic_ref<char8_t>::difference_type, char8_t>);
#endif // defined(__cpp_char8_t)
static_assert(atomic_ref_has_member_difference_type<char16_t>
&& std::is_same_v<std::atomic_ref<char16_t>::difference_type, char16_t>);
static_assert(atomic_ref_has_member_difference_type<char32_t>
&& std::is_same_v<std::atomic_ref<char32_t>::difference_type, char32_t>);
static_assert(atomic_ref_has_member_difference_type<wchar_t>
&& std::is_same_v<std::atomic_ref<wchar_t>::difference_type, wchar_t>);

static_assert(
atomic_ref_has_member_difference_type<float> && std::is_same_v<std::atomic_ref<float>::difference_type, float>);
static_assert(
atomic_ref_has_member_difference_type<double> && std::is_same_v<std::atomic_ref<double>::difference_type, double>);
static_assert(atomic_ref_has_member_difference_type<long double>
&& std::is_same_v<std::atomic_ref<long double>::difference_type, long double>);

static_assert(atomic_ref_has_member_difference_type<int*>
&& std::is_same_v<std::atomic_ref<int*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<bool*>
&& std::is_same_v<std::atomic_ref<bool*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<const int*>
&& std::is_same_v<std::atomic_ref<const int*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<volatile bool*>
&& std::is_same_v<std::atomic_ref<volatile bool*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<bigint*>
&& std::is_same_v<std::atomic_ref<bigint*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<const volatile int128*>
&& std::is_same_v<std::atomic_ref<const volatile int128*>::difference_type, std::ptrdiff_t>);

static_assert(atomic_ref_has_member_difference_type<void*>
&& std::is_same_v<std::atomic_ref<void*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<const void*>
&& std::is_same_v<std::atomic_ref<const void*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<volatile void*>
&& std::is_same_v<std::atomic_ref<volatile void*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<const volatile void*>
&& std::is_same_v<std::atomic_ref<const volatile void*>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<void (*)()>
&& std::is_same_v<std::atomic_ref<void (*)()>::difference_type, std::ptrdiff_t>);
static_assert(atomic_ref_has_member_difference_type<bigint (*)(int128)>
&& std::is_same_v<std::atomic_ref<bigint (*)(int128)>::difference_type, std::ptrdiff_t>);

static_assert(!atomic_ref_has_member_difference_type<bool>);
static_assert(!atomic_ref_has_member_difference_type<nullptr_t>);
static_assert(!atomic_ref_has_member_difference_type<bigint>);
static_assert(!atomic_ref_has_member_difference_type<int128>);

#if defined(TEST_CMPXCHG16B) && (defined(__clang__) || !defined(_M_X64))
// Skip Clang because it would require the -mcx16 compiler option.
// Skip non-x64 because _STD_ATOMIC_ALWAYS_USE_CMPXCHG16B is always 1 for ARM64, and is forbidden to be 1 for 32-bit.
int main() {}
#else // ^^^ skip test / run test vvv

#include <cassert>
#include <cstdint>
#include <execution>
#include <functional>
#include <memory>
#include <numeric>
#include <vector>


// code reuse of ../P1135R6_atomic_flag_test/test.cpp

Expand Down

0 comments on commit cf53dba

Please sign in to comment.