diff --git a/stl/inc/atomic b/stl/inc/atomic index 9f4da0a47e3..dd31109bcc8 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -473,7 +473,7 @@ void _Atomic_wait_direct( const _Value_type _Observed_bytes = _STD _Atomic_reinterpret_as<_Value_type>(_This->load(_Order)); if (_Expected_bytes != _Observed_bytes) { #if _CMPXCHG_MASK_OUT_PADDING_BITS - using _TVal = remove_reference_t<_Ty>; + using _TVal = _Remove_cvref_t<_Ty>; if constexpr (_Might_have_non_value_bits<_TVal>) { _Storage_for<_TVal> _Mask{_Form_mask}; const _Value_type _Mask_val = _STD _Atomic_reinterpret_as<_Value_type>(_Mask._Ref()); @@ -581,7 +581,7 @@ struct _Atomic_storage { // Provides operations common to all specializations of std::atomic, load, store, exchange, and CAS. // Locking version used when hardware has no atomic operations for sizeof(_Ty). - using _TVal = remove_reference_t<_Ty>; + using _TVal = _Remove_cvref_t<_Ty>; using _Guard = _Atomic_lock_guard::_Spinlock>; _Atomic_storage() = default; @@ -713,7 +713,7 @@ public: template struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics - using _TVal = remove_reference_t<_Ty>; + using _TVal = _Remove_cvref_t<_Ty>; _Atomic_storage() = default; @@ -814,7 +814,7 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics template struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics - using _TVal = remove_reference_t<_Ty>; + using _TVal = _Remove_cvref_t<_Ty>; _Atomic_storage() = default; @@ -914,7 +914,7 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics template struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics - using _TVal = remove_reference_t<_Ty>; + using _TVal = _Remove_cvref_t<_Ty>; _Atomic_storage() = default; @@ -1014,7 +1014,7 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics template struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics - using _TVal = remove_reference_t<_Ty>; + using _TVal = _Remove_cvref_t<_Ty>; _Atomic_storage() = default; @@ -1135,7 +1135,7 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics template struct _Atomic_storage<_Ty&, 16> { // lock-free using 16-byte intrinsics // TRANSITION, ABI: replace '_Ty&' with '_Ty' in this specialization - using _TVal = remove_reference_t<_Ty&>; + using _TVal = _Remove_cvref_t<_Ty&>; _Atomic_storage() = default; @@ -1615,11 +1615,17 @@ constexpr bool _Deprecate_non_lock_free_volatile = true; template _CXX20_DEPRECATE_VOLATILE constexpr bool _Deprecate_non_lock_free_volatile<_Ty, false> = true; +#if _HAS_CXX20 +#define _REQUIRES_CLAUSE(...) requires (__VA_ARGS__) +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +#define _REQUIRES_CLAUSE(...) +#endif // ^^^ !_HAS_CXX20 ^^^ + template struct _Atomic_integral_facade : _Atomic_integral<_Ty> { // provides operator overloads and other support for atomic integral specializations using _Base = _Atomic_integral<_Ty>; - using difference_type = _Ty; + using difference_type = remove_cv_t<_Ty>; using _Base::_Base; @@ -1747,87 +1753,88 @@ template struct _Atomic_integral_facade<_Ty&> : _Atomic_integral<_Ty&> { // provides operator overloads and other support for atomic integral specializations using _Base = _Atomic_integral<_Ty&>; - using difference_type = _Ty; + using difference_type = remove_cv_t<_Ty>; + using typename _Base::_TVal; using _Base::_Base; - _NODISCARD static _Ty _Negate(const _Ty _Value) noexcept { // returns two's complement negated value of _Value - return static_cast<_Ty>(0U - static_cast>(_Value)); + _NODISCARD static _TVal _Negate(const _TVal _Value) noexcept { // returns two's complement negated value of _Value + return static_cast<_TVal>(0U - static_cast>(_Value)); } - _Ty fetch_add(const _Ty _Operand) const noexcept { + _TVal fetch_add(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_add(_Operand); } - _Ty fetch_add(const _Ty _Operand, const memory_order _Order) const noexcept { + _TVal fetch_add(const _TVal _Operand, const memory_order _Order) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_add(_Operand, _Order); } - _Ty fetch_sub(const _Ty _Operand) const noexcept { + _TVal fetch_sub(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(_Negate(_Operand)); } - _Ty fetch_sub(const _Ty _Operand, const memory_order _Order) const noexcept { + _TVal fetch_sub(const _TVal _Operand, const memory_order _Order) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(_Negate(_Operand), _Order); } - _Ty operator++(int) const noexcept { + _TVal operator++(int) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::operator++(0); } - _Ty operator++() const noexcept { + _TVal operator++() const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::operator++(); } - _Ty operator--(int) const noexcept { + _TVal operator--(int) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::operator--(0); } - _Ty operator--() const noexcept { + _TVal operator--() const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::operator--(); } - _Ty operator+=(const _Ty _Operand) const noexcept { + _TVal operator+=(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return static_cast<_Ty>(fetch_add(_Operand) + _Operand); } - _Ty operator-=(const _Ty _Operand) const noexcept { + _TVal operator-=(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return static_cast<_Ty>(fetch_sub(_Operand) - _Operand); } - _Ty fetch_and(const _Ty _Operand) const noexcept { + _TVal fetch_and(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_and(_Operand); } - _Ty fetch_and(const _Ty _Operand, const memory_order _Order) const noexcept { + _TVal fetch_and(const _TVal _Operand, const memory_order _Order) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_and(_Operand, _Order); } - _Ty fetch_or(const _Ty _Operand) const noexcept { + _TVal fetch_or(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_or(_Operand); } - _Ty fetch_or(const _Ty _Operand, const memory_order _Order) const noexcept { + _TVal fetch_or(const _TVal _Operand, const memory_order _Order) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_or(_Operand, _Order); } - _Ty fetch_xor(const _Ty _Operand) const noexcept { + _TVal fetch_xor(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_xor(_Operand); } - _Ty fetch_xor(const _Ty _Operand, const memory_order _Order) const noexcept { + _TVal fetch_xor(const _TVal _Operand, const memory_order _Order) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return const_cast<_Atomic_integral_facade*>(this)->_Base::fetch_xor(_Operand, _Order); } - _Ty operator&=(const _Ty _Operand) const noexcept { + _TVal operator&=(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return static_cast<_Ty>(fetch_and(_Operand) & _Operand); } - _Ty operator|=(const _Ty _Operand) const noexcept { + _TVal operator|=(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return static_cast<_Ty>(fetch_or(_Operand) | _Operand); } - _Ty operator^=(const _Ty _Operand) const noexcept { + _TVal operator^=(const _TVal _Operand) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return static_cast<_Ty>(fetch_xor(_Operand) ^ _Operand); } }; @@ -1837,7 +1844,7 @@ template struct _Atomic_floating : _Atomic_storage<_Ty> { // provides atomic floating-point operations using _Base = _Atomic_storage<_Ty>; - using difference_type = _Ty; + using difference_type = remove_cv_t<_Ty>; using _Base::_Base; @@ -1891,12 +1898,15 @@ template struct _Atomic_floating<_Ty&> : _Atomic_storage<_Ty&> { // provides atomic floating-point operations using _Base = _Atomic_storage<_Ty&>; - using difference_type = _Ty; + using difference_type = remove_cv_t<_Ty>; + using typename _Base::_TVal; using _Base::_Base; - _Ty fetch_add(const _Ty _Operand, const memory_order _Order = memory_order_seq_cst) const noexcept { - _Ty _Temp{this->load(memory_order_relaxed)}; + _TVal fetch_add(const _TVal _Operand, const memory_order _Order = memory_order_seq_cst) const noexcept + requires (!is_const_v<_Ty>) + { + _TVal _Temp{this->load(memory_order_relaxed)}; while (!const_cast<_Atomic_floating*>(this)->_Base::compare_exchange_strong( _Temp, _Temp + _Operand, _Order)) { // keep trying } @@ -1904,8 +1914,10 @@ struct _Atomic_floating<_Ty&> : _Atomic_storage<_Ty&> { return _Temp; } - _Ty fetch_sub(const _Ty _Operand, const memory_order _Order = memory_order_seq_cst) const noexcept { - _Ty _Temp{this->load(memory_order_relaxed)}; + _TVal fetch_sub(const _TVal _Operand, const memory_order _Order = memory_order_seq_cst) const noexcept + requires (!is_const_v<_Ty>) + { + _TVal _Temp{this->load(memory_order_relaxed)}; while (!const_cast<_Atomic_floating*>(this)->_Base::compare_exchange_strong( _Temp, _Temp - _Operand, _Order)) { // keep trying } @@ -1913,11 +1925,15 @@ struct _Atomic_floating<_Ty&> : _Atomic_storage<_Ty&> { return _Temp; } - _Ty operator+=(const _Ty _Operand) const noexcept { + _TVal operator+=(const _TVal _Operand) const noexcept + requires (!is_const_v<_Ty>) + { return fetch_add(_Operand) + _Operand; } - _Ty operator-=(const _Ty _Operand) const noexcept { + _TVal operator-=(const _TVal _Operand) const noexcept + requires (!is_const_v<_Ty>) + { return fetch_sub(_Operand) - _Operand; } }; @@ -2023,10 +2039,12 @@ template struct _Atomic_pointer<_Ty&> : _Atomic_storage<_Ty&> { using _Base = _Atomic_storage<_Ty&>; using difference_type = ptrdiff_t; + using typename _Base::_TVal; using _Base::_Base; - _Ty fetch_add(const ptrdiff_t _Diff, const memory_order _Order = memory_order_seq_cst) const noexcept { + _TVal fetch_add(const ptrdiff_t _Diff, const memory_order _Order = memory_order_seq_cst) const noexcept + _REQUIRES_CLAUSE(!is_const_v<_Ty>) { const ptrdiff_t _Shift_bytes = static_cast(static_cast(_Diff) * sizeof(remove_pointer_t<_Ty>)); ptrdiff_t _Result; @@ -2040,39 +2058,42 @@ struct _Atomic_pointer<_Ty&> : _Atomic_storage<_Ty&> { return reinterpret_cast<_Ty>(_Result); } - _Ty fetch_sub(const ptrdiff_t _Diff) const noexcept { + _TVal fetch_sub(const ptrdiff_t _Diff) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(static_cast(0 - static_cast(_Diff))); } - _Ty fetch_sub(const ptrdiff_t _Diff, const memory_order _Order) const noexcept { + _TVal fetch_sub(const ptrdiff_t _Diff, const memory_order _Order) const noexcept + _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(static_cast(0 - static_cast(_Diff)), _Order); } - _Ty operator++(int) const noexcept { + _TVal operator++(int) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(1); } - _Ty operator++() const noexcept { + _TVal operator++() const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(1) + 1; } - _Ty operator--(int) const noexcept { + _TVal operator--(int) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(-1); } - _Ty operator--() const noexcept { + _TVal operator--() const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(-1) - 1; } - _Ty operator+=(const ptrdiff_t _Diff) const noexcept { + _TVal operator+=(const ptrdiff_t _Diff) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(_Diff) + _Diff; } - _Ty operator-=(const ptrdiff_t _Diff) const noexcept { + _TVal operator-=(const ptrdiff_t _Diff) const noexcept _REQUIRES_CLAUSE(!is_const_v<_Ty>) { return fetch_add(static_cast(0 - static_cast(_Diff))) - _Diff; } }; +#undef _REQUIRE_CLAUSE + template struct _Atomic_nonobject_pointer : _Atomic_storage<_Ty> { using _Base = _Atomic_storage<_Ty>; @@ -2081,15 +2102,22 @@ struct _Atomic_nonobject_pointer : _Atomic_storage<_Ty> { using _Base::_Base; }; +template +struct _Atomic_nonobject_pointer<_Ty&> : _Atomic_storage<_Ty&> { + using _Base = _Atomic_storage<_Ty&>; + + using _Base::_Base; +}; + #define ATOMIC_VAR_INIT(_Value) {_Value} template using _Choose_atomic_base2_t = - typename _Select && !is_same_v>::template _Apply<_Atomic_integral_facade<_Ty>, - typename _Select>::template _Apply< - typename _Select>>::template _Apply<_Atomic_pointer<_Ty>, - _Atomic_nonobject_pointer<_Ty>>, - _Atomic_storage<_Ty>>>; + typename _Select && !is_same_v>>::template _Apply< + _Atomic_integral_facade<_Ty>, typename _Select>::template _Apply< + typename _Select>>::template _Apply< + _Atomic_pointer<_Ty>, _Atomic_nonobject_pointer<_Ty>>, + _Atomic_storage<_Ty>>>; #if _HAS_CXX20 template @@ -2106,10 +2134,13 @@ private: using _Base = _Choose_atomic_base_t<_Ty>; public: - static_assert(is_trivially_copyable_v<_Ty> && is_copy_constructible_v<_Ty> && is_move_constructible_v<_Ty> - && is_copy_assignable_v<_Ty> && is_move_assignable_v<_Ty>, - "atomic requires T to be trivially copyable, copy constructible, move constructible, copy assignable, " - "and move assignable."); + static_assert(is_trivially_copyable_v<_Ty>, "atomic requires T to be trivially copyable."); + static_assert(is_copy_constructible_v<_Ty>, "atomic requires T to be copy constructible."); + static_assert(is_move_constructible_v<_Ty>, "atomic requires T to be move constructible."); + static_assert(is_copy_assignable_v<_Ty>, "atomic requires T to be copy assignable."); + static_assert(is_move_assignable_v<_Ty>, "atomic requires T to be move assignable."); + static_assert(!is_const_v<_Ty>, "atomic requires T to be non-const."); + static_assert(!is_volatile_v<_Ty>, "atomic requires T to be non-volatile."); using value_type = _Ty; @@ -2276,7 +2307,7 @@ private: public: static_assert(is_trivially_copyable_v<_Ty>, "atomic_ref requires T to be trivially copyable."); - using value_type = _Ty; + using value_type = remove_cv_t<_Ty>; explicit atomic_ref(_Ty& _Value) noexcept /* strengthened */ : _Base(_Value) { if constexpr (is_always_lock_free) { @@ -2295,66 +2326,97 @@ public: static constexpr size_t required_alignment = is_always_lock_free ? sizeof(_Ty) : alignof(_Ty); + static_assert(is_always_lock_free || !is_volatile_v<_Ty>, + "atomic_ref requires T to be non-volatile when it is not always lock-free."); + _NODISCARD bool is_lock_free() const noexcept { return is_always_lock_free; } - void store(const _Ty _Value) const noexcept { + void store(const value_type _Value) const noexcept + requires (!is_const_v<_Ty>) + { const_cast(this)->_Base::store(_Value); } - void store(const _Ty _Value, const memory_order _Order) const noexcept { + void store(const value_type _Value, const memory_order _Order) const noexcept + requires (!is_const_v<_Ty>) + { const_cast(this)->_Base::store(_Value, _Order); } - _Ty operator=(const _Ty _Value) const noexcept { + value_type operator=(const value_type _Value) const noexcept + requires (!is_const_v<_Ty>) + { store(_Value); return _Value; } - _Ty exchange(const _Ty _Value) const noexcept { + value_type exchange(const value_type _Value) const noexcept + requires (!is_const_v<_Ty>) + { return const_cast(this)->_Base::exchange(_Value); } - _Ty exchange(const _Ty _Value, const memory_order _Order) const noexcept { + value_type exchange(const value_type _Value, const memory_order _Order) const noexcept + requires (!is_const_v<_Ty>) + { return const_cast(this)->_Base::exchange(_Value, _Order); } - bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired) const noexcept { + bool compare_exchange_strong(value_type& _Expected, const value_type _Desired) const noexcept + requires (!is_const_v<_Ty>) + { return const_cast(this)->_Base::compare_exchange_strong(_Expected, _Desired); } - bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired, const memory_order _Order) const noexcept { + bool compare_exchange_strong( + value_type& _Expected, const value_type _Desired, const memory_order _Order) const noexcept + requires (!is_const_v<_Ty>) + { return const_cast(this)->_Base::compare_exchange_strong(_Expected, _Desired, _Order); } - bool compare_exchange_strong( - _Ty& _Expected, const _Ty _Desired, const memory_order _Success, const memory_order _Failure) const noexcept { + bool compare_exchange_strong(value_type& _Expected, const value_type _Desired, const memory_order _Success, + const memory_order _Failure) const noexcept + requires (!is_const_v<_Ty>) + { return compare_exchange_strong(_Expected, _Desired, _Combine_cas_memory_orders(_Success, _Failure)); } - bool compare_exchange_weak(_Ty& _Expected, const _Ty _Desired) const noexcept { + bool compare_exchange_weak(value_type& _Expected, const value_type _Desired) const noexcept + requires (!is_const_v<_Ty>) + { return compare_exchange_strong(_Expected, _Desired); } - bool compare_exchange_weak(_Ty& _Expected, const _Ty _Desired, const memory_order _Order) const noexcept { + bool compare_exchange_weak( + value_type& _Expected, const value_type _Desired, const memory_order _Order) const noexcept + requires (!is_const_v<_Ty>) + { return compare_exchange_strong(_Expected, _Desired, _Order); } - bool compare_exchange_weak( - _Ty& _Expected, const _Ty _Desired, const memory_order _Success, const memory_order _Failure) const noexcept { + bool compare_exchange_weak(value_type& _Expected, const value_type _Desired, const memory_order _Success, + const memory_order _Failure) const noexcept + requires (!is_const_v<_Ty>) + { return compare_exchange_strong(_Expected, _Desired, _Combine_cas_memory_orders(_Success, _Failure)); } - operator _Ty() const noexcept { + operator value_type() const noexcept { return this->load(); } - void notify_one() const noexcept { + void notify_one() const noexcept + requires (!is_const_v<_Ty>) + { const_cast(this)->_Base::notify_one(); } - void notify_all() const noexcept { + void notify_all() const noexcept + requires (!is_const_v<_Ty>) + { const_cast(this)->_Base::notify_all(); } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ce83ff45727..5f3c4a3f1f3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -80,6 +80,8 @@ // (__cpp_lib_freestanding_algorithm and __cpp_lib_freestanding_array only) // P2937R0 Freestanding Library: Remove strtok // P2968R2 Make std::ignore A First-Class Object +// P3323R1 Forbid atomic, Specify atomic_ref +// (for atomic) // _HAS_CXX17 directly controls: // P0005R4 not_fn() @@ -315,6 +317,8 @@ // P2909R4 Fix Formatting Of Code Units As Integers // P2997R1 Removing The Common Reference Requirement From The Indirectly Invocable Concepts // P3136R1 Retiring Niebloids +// P3323R1 Forbid atomic, Specify atomic_ref +// (for atomic_ref) // _HAS_CXX20 indirectly controls: // P0619R4 Removing C++17-Deprecated Features diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 3329d34b8f1..cdb0473ae6b 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -126,6 +126,9 @@ std/language.support/support.limits/support.limits.general/mdspan.version.compil # libc++ has not implemented P2937R0: "Freestanding Library: Remove strtok" std/language.support/support.limits/support.limits.general/cstring.version.compile.pass.cpp FAIL +# libc++ has not implemented P3323R1: "Forbid atomic, Specify atomic_ref" +std/atomics/atomics.ref/member_types.compile.pass.cpp FAIL + # Various bogosity (LLVM-D141004), warning C6011: Dereferencing NULL pointer # Note: The :1 (ASan) configuration doesn't run static analysis. std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_pair_const_lvalue_pair.pass.cpp:0 FAIL diff --git a/tests/std/tests/P0019R8_atomic_ref/test.cpp b/tests/std/tests/P0019R8_atomic_ref/test.cpp index 8edf08cfd9e..e304cfca17b 100644 --- a/tests/std/tests/P0019R8_atomic_ref/test.cpp +++ b/tests/std/tests/P0019R8_atomic_ref/test.cpp @@ -34,50 +34,191 @@ struct int128 { } }; -// Also test GH-4688 ": atomic_ref and atomic lack difference_type" +// Also test constraints and conditional existance of difference_type specified by +// P3323R1 "Forbid atomic, Specify atomic_ref". + +template +void test_atomic_ref_constraints_single() { // COMPILE-ONLY + using TD = std::remove_cv_t; + using AR = std::atomic_ref; + + static_assert(std::is_same_v); + static_assert(requires(const AR& r, std::memory_order ord) { + r.operator TD(); + { r.load() } -> std::same_as; + { r.load(ord) } -> std::same_as; + }); + + if constexpr (!std::is_const_v) { + static_assert(requires(const AR& r, TD v, TD& vx, std::memory_order ord1, std::memory_order ord2) { + { r.store(v) } -> std::same_as; + { r.store(v, ord1) } -> std::same_as; + { r = v } -> std::same_as; + { r.exchange(v) } -> std::same_as; + { r.exchange(v, ord1) } -> std::same_as; + { r.compare_exchange_weak(vx, v) } -> std::same_as; + { r.compare_exchange_weak(vx, v, ord1) } -> std::same_as; + { r.compare_exchange_weak(vx, v, ord1, ord2) } -> std::same_as; + { r.compare_exchange_strong(vx, v) } -> std::same_as; + { r.compare_exchange_strong(vx, v, ord1) } -> std::same_as; + { r.compare_exchange_strong(vx, v, ord1, ord2) } -> std::same_as; + { r.notify_one() } -> std::same_as; + { r.notify_all() } -> std::same_as; + }); + } else { + static_assert(!requires(const AR& r, TD v) { r.store(v); }); + static_assert(!requires(const AR& r, TD v, std::memory_order ord) { r.store(v, ord); }); + static_assert(!requires(const AR& r, TD v) { r = v; }); + static_assert(!requires(const AR& r, TD v) { r.exchange(v); }); + static_assert(!requires(const AR& r, TD v, std::memory_order ord) { r.exchange(v, ord); }); + static_assert(!requires(const AR& r, TD& v1, TD v2) { r.compare_exchange_weak(v1, v2); }); + static_assert( + !requires(const AR& r, TD& v1, TD v2, std::memory_order ord) { r.compare_exchange_weak(v1, v2, ord); }); + static_assert(!requires(const AR& r, TD& v1, TD v2, std::memory_order ord1, std::memory_order ord2) { + r.compare_exchange_weak(v1, v2, ord1, ord2); + }); + static_assert(!requires(const AR& r, TD& v1, TD v2) { r.compare_exchange_strong(v1, v2); }); + static_assert( + !requires(const AR& r, TD& v1, TD v2, std::memory_order ord) { r.compare_exchange_strong(v1, v2, ord); }); + static_assert(!requires(const AR& r, TD& v1, TD v2, std::memory_order ord1, std::memory_order ord2) { + r.compare_exchange_strong(v1, v2, ord1, ord2); + }); + static_assert(!requires(const AR& r) { r.notify_one(); }); + static_assert(!requires(const AR& r) { r.notify_all(); }); + } + + constexpr bool has_difference_type = (std::is_arithmetic_v && !std::is_same_v) + || (std::is_pointer_v && std::is_object_v>); + + if constexpr (has_difference_type) { + if constexpr (std::is_arithmetic_v) { + static_assert(std::is_same_v); + } else { + static_assert(std::is_same_v); + } + } else { + static_assert(!requires { typename AR::difference_type; }); + } + + if constexpr (has_difference_type && !std::is_const_v) { + static_assert(requires(const AR& r, AR::difference_type d, std::memory_order ord) { + { r.fetch_add(d) } -> std::same_as; + { r.fetch_add(d, ord) } -> std::same_as; + { r.fetch_sub(d) } -> std::same_as; + { r.fetch_sub(d, ord) } -> std::same_as; + { r += d } -> std::same_as; + { r -= d } -> std::same_as; + }); + } else { + static_assert(!requires(const AR& r, AR::difference_type d) { r.fetch_add(d); }); + static_assert(!requires(const AR& r, AR::difference_type d, std::memory_order ord) { r.fetch_add(d, ord); }); + static_assert(!requires(const AR& r, AR::difference_type d) { r.fetch_sub(d); }); + static_assert(!requires(const AR& r, AR::difference_type d, std::memory_order ord) { r.fetch_sub(d, ord); }); + static_assert(!requires(const AR& r, AR::difference_type d) { r += d; }); + static_assert(!requires(const AR& r, AR::difference_type d) { r -= d; }); + } + + if constexpr (has_difference_type && !std::is_floating_point_v && !std::is_const_v) { + static_assert(requires(const AR& r) { + { ++r } -> std::same_as; + { r++ } -> std::same_as; + { --r } -> std::same_as; + { r-- } -> std::same_as; + }); + } else { + static_assert(!requires(const AR& r) { ++r; }); + static_assert(!requires(const AR& r) { r++; }); + static_assert(!requires(const AR& r) { --r; }); + static_assert(!requires(const AR& r) { r--; }); + } + + if constexpr (std::is_integral_v && !std::is_same_v && !std::is_const_v) { + static_assert(requires(const AR& r, TD v, std::memory_order ord) { + { r.fetch_and(v) } -> std::same_as; + { r.fetch_and(v, ord) } -> std::same_as; + { r.fetch_or(v) } -> std::same_as; + { r.fetch_or(v, ord) } -> std::same_as; + { r.fetch_xor(v) } -> std::same_as; + { r.fetch_xor(v, ord) } -> std::same_as; + { r &= v } -> std::same_as; + { r |= v } -> std::same_as; + { r ^= v } -> std::same_as; + }); + } else { + static_assert(!requires(const AR& r, TD v) { r.fetch_and(v); }); + static_assert(!requires(const AR& r, TD v, std::memory_order ord) { r.fetch_and(v, ord); }); + static_assert(!requires(const AR& r, TD v) { r.fetch_or(v); }); + static_assert(!requires(const AR& r, TD v, std::memory_order ord) { r.fetch_or(v, ord); }); + static_assert(!requires(const AR& r, TD v) { r.fetch_xor(v); }); + static_assert(!requires(const AR& r, TD v, std::memory_order ord) { r.fetch_xor(v, ord); }); + static_assert(!requires(const AR& r, TD v) { r &= v; }); + static_assert(!requires(const AR& r, TD v) { r |= v; }); + static_assert(!requires(const AR& r, TD v) { r ^= v; }); + } +} + template -constexpr bool atomic_ref_has_member_difference_type = requires { typename std::atomic_ref::difference_type; }; - -static_assert(std::is_same_v::difference_type, signed char>); -static_assert(std::is_same_v::difference_type, short>); -static_assert(std::is_same_v::difference_type, int>); -static_assert(std::is_same_v::difference_type, long>); -static_assert(std::is_same_v::difference_type, long long>); -static_assert(std::is_same_v::difference_type, unsigned char>); -static_assert(std::is_same_v::difference_type, unsigned short>); -static_assert(std::is_same_v::difference_type, unsigned int>); -static_assert(std::is_same_v::difference_type, unsigned long>); -static_assert(std::is_same_v::difference_type, unsigned long long>); -static_assert(std::is_same_v::difference_type, char>); +void test_atomic_ref_constraints_cv() { // COMPILE-ONLY + static_assert(!std::is_const_v && !std::is_volatile_v); + test_atomic_ref_constraints_single(); + test_atomic_ref_constraints_single(); + if constexpr (std::atomic_ref::is_always_lock_free) { + test_atomic_ref_constraints_single(); + test_atomic_ref_constraints_single(); + } +} + +void test_atomic_ref_constraints() { // COMPILE-ONLY + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); #ifdef __cpp_char8_t -static_assert(std::is_same_v::difference_type, char8_t>); + test_atomic_ref_constraints_cv(); #endif // defined(__cpp_char8_t) -static_assert(std::is_same_v::difference_type, char16_t>); -static_assert(std::is_same_v::difference_type, char32_t>); -static_assert(std::is_same_v::difference_type, wchar_t>); - -static_assert(std::is_same_v::difference_type, float>); -static_assert(std::is_same_v::difference_type, double>); -static_assert(std::is_same_v::difference_type, long double>); - -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); - -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); -static_assert(std::is_same_v::difference_type, std::ptrdiff_t>); - -static_assert(!atomic_ref_has_member_difference_type); -static_assert(!atomic_ref_has_member_difference_type); -static_assert(!atomic_ref_has_member_difference_type); -static_assert(!atomic_ref_has_member_difference_type); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + + test_atomic_ref_constraints_cv(); + test_atomic_ref_constraints_cv(); +} // code reuse of ../P1135R6_atomic_flag_test/test.cpp