[PATCH] libstdc++: Handle cv-qualified types in atomic and atomic_ref [PR115402]
Jonathan Wakely
jwakely@redhat.com
Thu Oct 9 14:48:17 GMT 2025
On Fri, 26 Sep 2025 at 11:17 +0200, Tomasz Kamiński wrote:
>From: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
>
>Implements P3233R1 (DR for C++20/C++11, fixes LWG 4069 and 3508).
>
>This commit implements std::atomic_ref<cv T> support (LWG3508) as DR for
>C++20, by exctracting parts of the __atomic_ref class (that atomic_ref inherits
"extracting"
>rom) into a further base class (__atomic_ref_base):
"from"
>
>* __atomic_ref_base<const T> implements non-mutating (const) atomic API.
> Single base class is used, and the difference in is_always_lock_free and
> required_aligment values between types are handled by _S_is_always_lock_free,
> _S_required_aligment helper functions.
>* __atomic_ref_base<T> implements the common atomic APIs. The non-mutating
> operations are handled by inherting from __atomic_ref_base<const T> partial
> partial specialization. Tu support that __atomic_ref_base<const T> stores
> mutable pointer to T, and performs const_cast in constructor.
>* __atomic_ref<T, ....> inherits from __atomic_ref_base<T>, and implement
> type-specific mutable APIs (fetch_add, -=, ...) and difference_type member
> type.
>* __atomic_ref<const T, ...> inherits from __atomic_ref_base<const T>
> and adds different_type member, whose presence and denoted type depends
> on T.
>
>The __atomic_ref specialization selection is adjusted to handle cv-qualified
>bool (add remove_cv_t) and pointer types. To handle the later, additional
>constant template parameter is introduced.
>
>The atomic wait and notify operations are currently not supported for volatile
>types, to signal that static assert is added to corresponding methods of
>atomic_ref.
>
>At the same time, disable support for cv-qualified types in std::atomic
>(for instance, std::atomic<volatile T> isn't meaningful; one should use
>volatile std::atomic<T>), again as per the paper, resolving LWG4069 as DR
>for C++11. This only affects atomic<volatile T>, as specialization
>atomic with const-qualifed types was already producing an compile-time
>error.
>
> PR libstdc++/115402
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/atomic_base.h (__atomic_ref_base<const _Tp>)
> (__atomic_ref_base<_Tp>): Define by extracting common methods
> from atomic_ref specializations.
> (__atomic_ref<_Tp, In, Fp, Pt>): Inherit from __atomic_ref_base
> and remove extracted method.
> (__atomic_ref<const _Tp, In, Fp, Pt>): Define.
> * include/std/atomic (std::atomic): Added an
> * testsuite/29_atomics/atomic/requirements/types_neg.cc:
> Add test for volatile qualified types.
> * testsuite/29_atomics/atomic_ref/bool.cc: Move the content
> to op_support.cc, add test for bool.
> * testsuite/29_atomics/atomic_ref/op_support.cc: New test
> expanded from atomic_ref/bool.cc.
> * testsuite/29_atomics/atomic_ref/cv_qual.cc: New test.
> * testsuite/29_atomics/atomic_ref/requirements_neg.cc: New test.
> * testsuite/29_atomics/atomic_ref/deduction.cc: Add tests for
> cv-qualified types.
> * testsuite/29_atomics/atomic_ref/float.cc: Likewise.
> * testsuite/29_atomics/atomic_ref/generic.cc: Likewise.
> * testsuite/29_atomics/atomic_ref/integral.cc: Likewise.
> * testsuite/29_atomics/atomic_ref/pointer.cc: Likewise.
> * testsuite/29_atomics/atomic_ref/requirements.cc: Likewise.
> * testsuite/29_atomics/atomic_ref/wait_notify.cc: Add tests for
> const qualified types.
>
>Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
>Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
>Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
>---
>The changes and split between original Giusepee patch, and my changes
>can be found here:
>https://forge.sourceware.org/gcc/gcc-TEST/pulls/85
>
>Tested on x86-64-linux locally. Testing on powerpc64le-linux.
>OK for trunk when test passed.
>
> libstdc++-v3/include/bits/atomic_base.h | 566 ++++++------------
> libstdc++-v3/include/std/atomic | 5 +
> .../atomic/requirements/types_neg.cc | 4 +-
> .../testsuite/29_atomics/atomic_ref/bool.cc | 94 ++-
> .../29_atomics/atomic_ref/cv_qual.cc | 94 +++
> .../29_atomics/atomic_ref/deduction.cc | 33 +-
> .../testsuite/29_atomics/atomic_ref/float.cc | 21 +-
> .../29_atomics/atomic_ref/generic.cc | 6 +
> .../29_atomics/atomic_ref/integral.cc | 6 +
> .../29_atomics/atomic_ref/op_support.cc | 113 ++++
> .../29_atomics/atomic_ref/pointer.cc | 6 +
> .../29_atomics/atomic_ref/requirements.cc | 68 ++-
> .../29_atomics/atomic_ref/requirements_neg.cc | 34 ++
> .../29_atomics/atomic_ref/wait_notify.cc | 10 +
> 14 files changed, 621 insertions(+), 439 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc
> create mode 100644 libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc
>
>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>index 92d1269493f..5b8c2d49e9b 100644
>--- a/libstdc++-v3/include/bits/atomic_base.h
>+++ b/libstdc++-v3/include/bits/atomic_base.h
>@@ -1508,211 +1508,146 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> };
> #undef _GLIBCXX20_INIT
>
>- template<typename _Tp,
>- bool = is_integral_v<_Tp> && !is_same_v<_Tp, bool>,
>- bool = is_floating_point_v<_Tp>>
>- struct __atomic_ref;
>+ // __atomic_ref_base<const _Tp> provides the common APIs for const and
>+ // types,
Should this be "const and non-const types"?
>+ // __atomic_ref_base<_Tp> inhserits from __atomic_ref_base<const _Tp>,
"inherits"
>+ // and provides the commonn APIs implementing constrains in [atomic.ref].
"common" and "constraints"
>+ // __atomic_ref<_Tp> inherits from __atomic_ref_base<_Tp> (const or not-const)
"non-const"
>+ // adds type specific mutating APIs.
Hyphen in "type-specific" please
>+ // atomic_ref inherits from __atomic_ref;
>+
>+ template<typename _Tp>
>+ struct __atomic_ref_base;
>
>- // base class for non-integral, non-floating-point, non-pointer types
> template<typename _Tp>
>- struct __atomic_ref<_Tp, false, false>
>+ struct __atomic_ref_base<const _Tp>
> {
>- static_assert(is_trivially_copyable_v<_Tp>);
>+ private:
>+ using _Vt = remove_cv_t<_Tp>;
>+
>+ static consteval bool
>+ _S_is_always_lock_free()
>+ {
>+ if constexpr (is_pointer_v<_Vt>)
>+ return ATOMIC_POINTER_LOCK_FREE == 2;
>+ else
>+ return __atomic_always_lock_free(sizeof(_Vt), 0);
>+ }
>
>- // 1/2/4/8/16-byte types must be aligned to at least their size.
>- static constexpr int _S_min_alignment
>- = (sizeof(_Tp) & (sizeof(_Tp) - 1)) || sizeof(_Tp) > 16
>- ? 0 : sizeof(_Tp);
>+ static consteval int
>+ _S_required_aligment()
>+ {
>+ if constexpr (is_floating_point_v<_Vt> || is_pointer_v<_Vt>)
>+ return alignof(_Vt);
>+ else if constexpr ((sizeof(_Vt) & (sizeof(_Vt) - 1)) || sizeof(_Vt) > 16)
>+ return alignof(_Vt);
>+ else
>+ // 1/2/4/8/16-byte types, including integral types,
>+ // must be aligned to at least their size.
>+ return (sizeof(_Vt) > alignof(_Vt)) ? sizeof(_Vt) : alignof(_Vt);
>+ }
>
> public:
>- using value_type = _Tp;
>+ using value_type = _Vt;
>+ static_assert(is_trivially_copyable_v<value_type>);
>
>- static constexpr bool is_always_lock_free
>- = __atomic_always_lock_free(sizeof(_Tp), 0);
>+ static constexpr bool is_always_lock_free = _S_is_always_lock_free();
>+ static_assert(is_always_lock_free || !is_volatile_v<_Tp>,
>+ "atomic_ref of volatile-qualified type is only sypported if operations are lock-free");
"supported", butI'd like to phrase this as a "must be ..." sentence
rather than "is only supported".
How about:
"atomic operations on volatile T must be lock-free"
>
>- static constexpr size_t required_alignment
>- = _S_min_alignment > alignof(_Tp) ? _S_min_alignment : alignof(_Tp);
>+ static constexpr size_t required_alignment = _S_required_aligment();
>
>- __atomic_ref& operator=(const __atomic_ref&) = delete;
>+ __atomic_ref_base() = delete;
>+ __atomic_ref_base& operator=(const __atomic_ref_base&) = delete;
>
> explicit
>- __atomic_ref(_Tp& __t) : _M_ptr(std::__addressof(__t))
>+ __atomic_ref_base(const _Tp& __t)
>+ : _M_ptr(const_cast<_Tp*>(std::__addressof(__t)))
This can be replaced with std::addressof (previously __addressof was
more efficient but we fixed that).
> {
> __glibcxx_assert(((__UINTPTR_TYPE__)_M_ptr % required_alignment) == 0);
> }
>
>- __atomic_ref(const __atomic_ref&) noexcept = default;
>-
>- _Tp
>- operator=(_Tp __t) const noexcept
>- {
>- this->store(__t);
>- return __t;
>- }
>+ __atomic_ref_base(const __atomic_ref_base&) noexcept = default;
>
>- operator _Tp() const noexcept { return this->load(); }
>+ operator value_type() const noexcept { return this->load(); }
>
> bool
> is_lock_free() const noexcept
> { return __atomic_impl::is_lock_free<sizeof(_Tp), required_alignment>(); }
>
>- void
>- store(_Tp __t, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::store(_M_ptr, __t, __m); }
>-
>- _Tp
>+ value_type
> load(memory_order __m = memory_order_seq_cst) const noexcept
> { return __atomic_impl::load(_M_ptr, __m); }
>
>- _Tp
>- exchange(_Tp __desired, memory_order __m = memory_order_seq_cst)
>- const noexcept
>- { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
>-
>- bool
>- compare_exchange_weak(_Tp& __expected, _Tp __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>- {
>- return __atomic_impl::compare_exchange_weak<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>- }
>-
>- bool
>- compare_exchange_strong(_Tp& __expected, _Tp __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>- {
>- return __atomic_impl::compare_exchange_strong<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>- }
>-
>- bool
>- compare_exchange_weak(_Tp& __expected, _Tp __desired,
>- memory_order __order = memory_order_seq_cst)
>- const noexcept
>- {
>- return compare_exchange_weak(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>- }
>-
>- bool
>- compare_exchange_strong(_Tp& __expected, _Tp __desired,
>- memory_order __order = memory_order_seq_cst)
>- const noexcept
>- {
>- return compare_exchange_strong(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>- }
>-
> #if __glibcxx_atomic_wait
> _GLIBCXX_ALWAYS_INLINE void
>- wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::wait(_M_ptr, __old, __m); }
>-
>- // TODO add const volatile overload
>-
>- _GLIBCXX_ALWAYS_INLINE void
>- notify_one() const noexcept
>- { __atomic_impl::notify_one(_M_ptr); }
>-
>- // TODO add const volatile overload
>-
>- _GLIBCXX_ALWAYS_INLINE void
>- notify_all() const noexcept
>- { __atomic_impl::notify_all(_M_ptr); }
>-
>- // TODO add const volatile overload
>+ wait(value_type __old, memory_order __m = memory_order_seq_cst) const noexcept
>+ {
>+ // TODO remove when volatile is supported
>+ static_assert(!is_volatile_v<_Tp>, "atomics wait on volatile are not supported");
Since this static assert is about a missing feature in the
implementation, I think the wording doesn't need to follow the usual
"must" form, but it should be "atomic waits" not "atomics wait" (same
change in two more places below).
>+ __atomic_impl::wait(_M_ptr, __old, __m);
>+ }
> #endif // __glibcxx_atomic_wait
>
>- private:
>+ protected:
> _Tp* _M_ptr;
> };
>
>- // base class for atomic_ref<integral-type>
> template<typename _Tp>
>- struct __atomic_ref<_Tp, true, false>
>+ struct __atomic_ref_base
>+ : __atomic_ref_base<const _Tp>
> {
>- static_assert(is_integral_v<_Tp>);
>-
>- public:
>- using value_type = _Tp;
>- using difference_type = value_type;
>-
>- static constexpr bool is_always_lock_free
>- = __atomic_always_lock_free(sizeof(_Tp), 0);
>-
>- static constexpr size_t required_alignment
>- = sizeof(_Tp) > alignof(_Tp) ? sizeof(_Tp) : alignof(_Tp);
>-
>- __atomic_ref() = delete;
>- __atomic_ref& operator=(const __atomic_ref&) = delete;
>+ using value_type = typename __atomic_ref_base<const _Tp>::value_type;
>
> explicit
>- __atomic_ref(_Tp& __t) : _M_ptr(&__t)
>- {
>- __glibcxx_assert(((__UINTPTR_TYPE__)_M_ptr % required_alignment) == 0);
>- }
>+ __atomic_ref_base(_Tp& __t) : __atomic_ref_base<const _Tp>(__t)
>+ { }
>
>- __atomic_ref(const __atomic_ref&) noexcept = default;
>-
>- _Tp
>- operator=(_Tp __t) const noexcept
>+ value_type
>+ operator=(value_type __t) const noexcept
> {
> this->store(__t);
> return __t;
> }
>
>- operator _Tp() const noexcept { return this->load(); }
>-
>- bool
>- is_lock_free() const noexcept
>- {
>- return __atomic_impl::is_lock_free<sizeof(_Tp), required_alignment>();
>- }
>-
> void
>- store(_Tp __t, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::store(_M_ptr, __t, __m); }
>-
>- _Tp
>- load(memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::load(_M_ptr, __m); }
>+ store(value_type __t, memory_order __m = memory_order_seq_cst) const noexcept
>+ { __atomic_impl::store(this->_M_ptr, __t, __m); }
>
>- _Tp
>- exchange(_Tp __desired,
>- memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
>+ value_type
>+ exchange(value_type __desired, memory_order __m = memory_order_seq_cst)
>+ const noexcept
>+ { return __atomic_impl::exchange(this->_M_ptr, __desired, __m); }
>
> bool
>- compare_exchange_weak(_Tp& __expected, _Tp __desired,
>+ compare_exchange_weak(value_type& __expected, value_type __desired,
> memory_order __success,
> memory_order __failure) const noexcept
> {
> return __atomic_impl::compare_exchange_weak<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>+ this->_M_ptr, __expected, __desired, __success, __failure);
> }
>
> bool
>- compare_exchange_strong(_Tp& __expected, _Tp __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>+ compare_exchange_strong(value_type& __expected, value_type __desired,
>+ memory_order __success,
>+ memory_order __failure) const noexcept
> {
> return __atomic_impl::compare_exchange_strong<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>+ this->_M_ptr, __expected, __desired, __success, __failure);
> }
>
> bool
>- compare_exchange_weak(_Tp& __expected, _Tp __desired,
>+ compare_exchange_weak(value_type& __expected, value_type __desired,
> memory_order __order = memory_order_seq_cst)
> const noexcept
> {
> return compare_exchange_weak(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>+ __cmpexch_failure_order(__order));
> }
>
> bool
>- compare_exchange_strong(_Tp& __expected, _Tp __desired,
>+ compare_exchange_strong(value_type& __expected, value_type __desired,
> memory_order __order = memory_order_seq_cst)
> const noexcept
> {
>@@ -1721,49 +1656,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> #if __glibcxx_atomic_wait
>- _GLIBCXX_ALWAYS_INLINE void
>- wait(_Tp __old, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::wait(_M_ptr, __old, __m); }
>-
>- // TODO add const volatile overload
>-
> _GLIBCXX_ALWAYS_INLINE void
> notify_one() const noexcept
>- { __atomic_impl::notify_one(_M_ptr); }
>-
>- // TODO add const volatile overload
>+ {
>+ // TODO remove when volatile is supported
>+ static_assert(!is_volatile_v<_Tp>, "atomics wait on volatile are not supported");
>+ __atomic_impl::notify_one(this->_M_ptr);
>+ }
>
> _GLIBCXX_ALWAYS_INLINE void
> notify_all() const noexcept
>- { __atomic_impl::notify_all(_M_ptr); }
>-
>- // TODO add const volatile overload
>+ {
>+ // TODO remove when volatile is supported
>+ static_assert(!is_volatile_v<_Tp>, "atomics wait on volatile are not supported");
>+ __atomic_impl::notify_all(this->_M_ptr);
>+ }
> #endif // __glibcxx_atomic_wait
>+ };
>+
>+ template<typename _Tp,
>+ bool = is_integral_v<_Tp> && !is_same_v<remove_cv_t<_Tp>, bool>,
>+ bool = is_floating_point_v<_Tp>,
>+ bool = is_pointer_v<_Tp>>
>+ struct __atomic_ref;
>+
>+ // base class for non-integral, non-floating-point, non-pointer types
>+ template<typename _Tp>
>+ struct __atomic_ref<_Tp, false, false, false>
>+ : __atomic_ref_base<_Tp>
>+ {
>+ using __atomic_ref_base<_Tp>::__atomic_ref_base;
>+ using __atomic_ref_base<_Tp>::operator=;
>+ };
>+
>+ template<typename _Tp>
>+ struct __atomic_ref<const _Tp, false, false, false>
>+ : __atomic_ref_base<const _Tp>
>+ {
>+ using __atomic_ref_base<const _Tp>::__atomic_ref_base;
>+ };
>+
>+ // base class for atomic_ref<integral-type>
>+ template<typename _Tp>
>+ struct __atomic_ref<_Tp, true, false, false>
>+ : __atomic_ref_base<_Tp>
>+ {
>+ using value_type = typename __atomic_ref_base<_Tp>::value_type;
>+ using difference_type = value_type;
>+
>+ using __atomic_ref_base<_Tp>::__atomic_ref_base;
>+ using __atomic_ref_base<_Tp>::operator=;
>
> value_type
> fetch_add(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_add(_M_ptr, __i, __m); }
>+ { return __atomic_impl::fetch_add(this->_M_ptr, __i, __m); }
>
> value_type
> fetch_sub(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_sub(_M_ptr, __i, __m); }
>+ { return __atomic_impl::fetch_sub(this->_M_ptr, __i, __m); }
>
> value_type
> fetch_and(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_and(_M_ptr, __i, __m); }
>+ { return __atomic_impl::fetch_and(this->_M_ptr, __i, __m); }
>
> value_type
> fetch_or(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_or(_M_ptr, __i, __m); }
>+ { return __atomic_impl::fetch_or(this->_M_ptr, __i, __m); }
>
> value_type
> fetch_xor(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_xor(_M_ptr, __i, __m); }
>+ { return __atomic_impl::fetch_xor(this->_M_ptr, __i, __m); }
>
> _GLIBCXX_ALWAYS_INLINE value_type
> operator++(int) const noexcept
>@@ -1775,284 +1742,98 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> value_type
> operator++() const noexcept
>- { return __atomic_impl::__add_fetch(_M_ptr, value_type(1)); }
>+ { return __atomic_impl::__add_fetch(this->_M_ptr, value_type(1)); }
>
> value_type
> operator--() const noexcept
>- { return __atomic_impl::__sub_fetch(_M_ptr, value_type(1)); }
>+ { return __atomic_impl::__sub_fetch(this->_M_ptr, value_type(1)); }
>
> value_type
> operator+=(value_type __i) const noexcept
>- { return __atomic_impl::__add_fetch(_M_ptr, __i); }
>+ { return __atomic_impl::__add_fetch(this->_M_ptr, __i); }
>
> value_type
> operator-=(value_type __i) const noexcept
>- { return __atomic_impl::__sub_fetch(_M_ptr, __i); }
>+ { return __atomic_impl::__sub_fetch(this->_M_ptr, __i); }
>
> value_type
> operator&=(value_type __i) const noexcept
>- { return __atomic_impl::__and_fetch(_M_ptr, __i); }
>+ { return __atomic_impl::__and_fetch(this->_M_ptr, __i); }
>
> value_type
> operator|=(value_type __i) const noexcept
>- { return __atomic_impl::__or_fetch(_M_ptr, __i); }
>+ { return __atomic_impl::__or_fetch(this->_M_ptr, __i); }
>
> value_type
> operator^=(value_type __i) const noexcept
>- { return __atomic_impl::__xor_fetch(_M_ptr, __i); }
>+ { return __atomic_impl::__xor_fetch(this->_M_ptr, __i); }
>+ };
>
>- private:
>- _Tp* _M_ptr;
>+ template<typename _Tp>
>+ struct __atomic_ref<const _Tp, true, false, false>
>+ : __atomic_ref_base<const _Tp>
>+ {
>+ using difference_type = typename __atomic_ref_base<const _Tp>::value_type;
>+ using __atomic_ref_base<const _Tp>::__atomic_ref_base;
> };
>
> // base class for atomic_ref<floating-point-type>
> template<typename _Fp>
>- struct __atomic_ref<_Fp, false, true>
>+ struct __atomic_ref<_Fp, false, true, false>
>+ : __atomic_ref_base<_Fp>
> {
>- static_assert(is_floating_point_v<_Fp>);
>-
>- public:
>- using value_type = _Fp;
>+ using value_type = typename __atomic_ref_base<_Fp>::value_type;
> using difference_type = value_type;
>
>- static constexpr bool is_always_lock_free
>- = __atomic_always_lock_free(sizeof(_Fp), 0);
>-
>- static constexpr size_t required_alignment = __alignof__(_Fp);
>-
>- __atomic_ref() = delete;
>- __atomic_ref& operator=(const __atomic_ref&) = delete;
>-
>- explicit
>- __atomic_ref(_Fp& __t) : _M_ptr(&__t)
>- {
>- __glibcxx_assert(((__UINTPTR_TYPE__)_M_ptr % required_alignment) == 0);
>- }
>-
>- __atomic_ref(const __atomic_ref&) noexcept = default;
>-
>- _Fp
>- operator=(_Fp __t) const noexcept
>- {
>- this->store(__t);
>- return __t;
>- }
>-
>- operator _Fp() const noexcept { return this->load(); }
>-
>- bool
>- is_lock_free() const noexcept
>- {
>- return __atomic_impl::is_lock_free<sizeof(_Fp), required_alignment>();
>- }
>-
>- void
>- store(_Fp __t, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::store(_M_ptr, __t, __m); }
>-
>- _Fp
>- load(memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::load(_M_ptr, __m); }
>-
>- _Fp
>- exchange(_Fp __desired,
>- memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
>-
>- bool
>- compare_exchange_weak(_Fp& __expected, _Fp __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>- {
>- return __atomic_impl::compare_exchange_weak<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>- }
>-
>- bool
>- compare_exchange_strong(_Fp& __expected, _Fp __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>- {
>- return __atomic_impl::compare_exchange_strong<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>- }
>-
>- bool
>- compare_exchange_weak(_Fp& __expected, _Fp __desired,
>- memory_order __order = memory_order_seq_cst)
>- const noexcept
>- {
>- return compare_exchange_weak(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>- }
>-
>- bool
>- compare_exchange_strong(_Fp& __expected, _Fp __desired,
>- memory_order __order = memory_order_seq_cst)
>- const noexcept
>- {
>- return compare_exchange_strong(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>- }
>-
>-#if __glibcxx_atomic_wait
>- _GLIBCXX_ALWAYS_INLINE void
>- wait(_Fp __old, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::wait(_M_ptr, __old, __m); }
>-
>- // TODO add const volatile overload
>-
>- _GLIBCXX_ALWAYS_INLINE void
>- notify_one() const noexcept
>- { __atomic_impl::notify_one(_M_ptr); }
>-
>- // TODO add const volatile overload
>-
>- _GLIBCXX_ALWAYS_INLINE void
>- notify_all() const noexcept
>- { __atomic_impl::notify_all(_M_ptr); }
>-
>- // TODO add const volatile overload
>-#endif // __glibcxx_atomic_wait
>+ using __atomic_ref_base<_Fp>::__atomic_ref_base;
>+ using __atomic_ref_base<_Fp>::operator=;
>
> value_type
> fetch_add(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::__fetch_add_flt(_M_ptr, __i, __m); }
>+ { return __atomic_impl::__fetch_add_flt(this->_M_ptr, __i, __m); }
>
> value_type
> fetch_sub(value_type __i,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::__fetch_sub_flt(_M_ptr, __i, __m); }
>+ { return __atomic_impl::__fetch_sub_flt(this->_M_ptr, __i, __m); }
>
> value_type
> operator+=(value_type __i) const noexcept
>- { return __atomic_impl::__add_fetch_flt(_M_ptr, __i); }
>+ { return __atomic_impl::__add_fetch_flt(this->_M_ptr, __i); }
>
> value_type
> operator-=(value_type __i) const noexcept
>- { return __atomic_impl::__sub_fetch_flt(_M_ptr, __i); }
>+ { return __atomic_impl::__sub_fetch_flt(this->_M_ptr, __i); }
>+ };
>
>- private:
>- _Fp* _M_ptr;
>+ template<typename _Fp>
>+ struct __atomic_ref<const _Fp, false, true, false>
>+ : __atomic_ref_base<const _Fp>
>+ {
>+ using difference_type = typename __atomic_ref_base<const _Fp>::value_type;
>+ using __atomic_ref_base<const _Fp>::__atomic_ref_base;
> };
>
> // base class for atomic_ref<pointer-type>
>- template<typename _Tp>
>- struct __atomic_ref<_Tp*, false, false>
>+ template<typename _Pt>
>+ struct __atomic_ref<_Pt, false, false, true>
>+ : __atomic_ref_base<_Pt>
> {
>- public:
>- using value_type = _Tp*;
>+ using value_type = typename __atomic_ref_base<_Pt>::value_type;
> using difference_type = ptrdiff_t;
>
>- static constexpr bool is_always_lock_free = ATOMIC_POINTER_LOCK_FREE == 2;
>-
>- static constexpr size_t required_alignment = __alignof__(_Tp*);
>-
>- __atomic_ref() = delete;
>- __atomic_ref& operator=(const __atomic_ref&) = delete;
>-
>- explicit
>- __atomic_ref(_Tp*& __t) : _M_ptr(std::__addressof(__t))
>- {
>- __glibcxx_assert(((__UINTPTR_TYPE__)_M_ptr % required_alignment) == 0);
>- }
>-
>- __atomic_ref(const __atomic_ref&) noexcept = default;
>-
>- _Tp*
>- operator=(_Tp* __t) const noexcept
>- {
>- this->store(__t);
>- return __t;
>- }
>-
>- operator _Tp*() const noexcept { return this->load(); }
>-
>- bool
>- is_lock_free() const noexcept
>- {
>- return __atomic_impl::is_lock_free<sizeof(_Tp*), required_alignment>();
>- }
>-
>- void
>- store(_Tp* __t, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::store(_M_ptr, __t, __m); }
>-
>- _Tp*
>- load(memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::load(_M_ptr, __m); }
>-
>- _Tp*
>- exchange(_Tp* __desired,
>- memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
>-
>- bool
>- compare_exchange_weak(_Tp*& __expected, _Tp* __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>- {
>- return __atomic_impl::compare_exchange_weak<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>- }
>-
>- bool
>- compare_exchange_strong(_Tp*& __expected, _Tp* __desired,
>- memory_order __success,
>- memory_order __failure) const noexcept
>- {
>- return __atomic_impl::compare_exchange_strong<true>(
>- _M_ptr, __expected, __desired, __success, __failure);
>- }
>-
>- bool
>- compare_exchange_weak(_Tp*& __expected, _Tp* __desired,
>- memory_order __order = memory_order_seq_cst)
>- const noexcept
>- {
>- return compare_exchange_weak(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>- }
>-
>- bool
>- compare_exchange_strong(_Tp*& __expected, _Tp* __desired,
>- memory_order __order = memory_order_seq_cst)
>- const noexcept
>- {
>- return compare_exchange_strong(__expected, __desired, __order,
>- __cmpexch_failure_order(__order));
>- }
>-
>-#if __glibcxx_atomic_wait
>- _GLIBCXX_ALWAYS_INLINE void
>- wait(_Tp* __old, memory_order __m = memory_order_seq_cst) const noexcept
>- { __atomic_impl::wait(_M_ptr, __old, __m); }
>-
>- // TODO add const volatile overload
>-
>- _GLIBCXX_ALWAYS_INLINE void
>- notify_one() const noexcept
>- { __atomic_impl::notify_one(_M_ptr); }
>-
>- // TODO add const volatile overload
>-
>- _GLIBCXX_ALWAYS_INLINE void
>- notify_all() const noexcept
>- { __atomic_impl::notify_all(_M_ptr); }
>-
>- // TODO add const volatile overload
>-#endif // __glibcxx_atomic_wait
>-
>+ using __atomic_ref_base<_Pt>::__atomic_ref_base;
>+ using __atomic_ref_base<_Pt>::operator=;
> _GLIBCXX_ALWAYS_INLINE value_type
> fetch_add(difference_type __d,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_add(_M_ptr, _S_type_size(__d), __m); }
>+ { return __atomic_impl::fetch_add(this->_M_ptr, _S_type_size(__d), __m); }
>
> _GLIBCXX_ALWAYS_INLINE value_type
> fetch_sub(difference_type __d,
> memory_order __m = memory_order_seq_cst) const noexcept
>- { return __atomic_impl::fetch_sub(_M_ptr, _S_type_size(__d), __m); }
>+ { return __atomic_impl::fetch_sub(this->_M_ptr, _S_type_size(__d), __m); }
>
> value_type
> operator++(int) const noexcept
>@@ -2065,36 +1846,43 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> value_type
> operator++() const noexcept
> {
>- return __atomic_impl::__add_fetch(_M_ptr, _S_type_size(1));
>+ return __atomic_impl::__add_fetch(this->_M_ptr, _S_type_size(1));
> }
>
> value_type
> operator--() const noexcept
> {
>- return __atomic_impl::__sub_fetch(_M_ptr, _S_type_size(1));
>+ return __atomic_impl::__sub_fetch(this->_M_ptr, _S_type_size(1));
> }
>
> value_type
> operator+=(difference_type __d) const noexcept
> {
>- return __atomic_impl::__add_fetch(_M_ptr, _S_type_size(__d));
>+ return __atomic_impl::__add_fetch(this->_M_ptr, _S_type_size(__d));
> }
>
> value_type
> operator-=(difference_type __d) const noexcept
> {
>- return __atomic_impl::__sub_fetch(_M_ptr, _S_type_size(__d));
>+ return __atomic_impl::__sub_fetch(this->_M_ptr, _S_type_size(__d));
> }
>
> private:
> static constexpr ptrdiff_t
> _S_type_size(ptrdiff_t __d) noexcept
> {
>- static_assert(is_object_v<_Tp>);
>- return __d * sizeof(_Tp);
>+ using _Et = remove_pointer_t<value_type>;
>+ static_assert(is_object_v<_Et>);
>+ return __d * sizeof(_Et);
> }
>+ };
>
>- _Tp** _M_ptr;
>+ template<typename _Pt>
>+ struct __atomic_ref<const _Pt, false, false, true>
>+ : __atomic_ref_base<const _Pt>
>+ {
>+ using difference_type = ptrdiff_t;
>+ using __atomic_ref_base<const _Pt>::__atomic_ref_base;
> };
> #endif // C++2a
>
>diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
>index 9b1aca0fc09..1ea28b1d742 100644
>--- a/libstdc++-v3/include/std/atomic
>+++ b/libstdc++-v3/include/std/atomic
>@@ -217,6 +217,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static_assert(sizeof(_Tp) > 0,
> "Incomplete or zero-sized types are not supported");
>
>+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
>+ // 4069. std::atomic<volatile T> should be ill-formed
>+ static_assert(is_same<_Tp, typename remove_cv<_Tp>::type>::value,
>+ "cv-qualified types are not supported");
Maybe:
"template argument for std::atomic must not be const or volatile"
OK with those changes, thanks (and thanks to Giuseppe).
>+
> #if __cplusplus > 201703L
> static_assert(is_copy_constructible_v<_Tp>);
> static_assert(is_move_constructible_v<_Tp>);
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc b/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc
>index cfe44d255ca..b9105481006 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc
>@@ -19,7 +19,9 @@
>
> #include <atomic>
>
>-std::atomic<const int> a; // { dg-error "here" }
>+std::atomic<const int> ca; // { dg-error "here" }
>+std::atomic<volatile int> va; // { dg-error "here" }
>+std::atomic<const volatile int> cva; // { dg-error "here" }
> // { dg-error "assignment to read-only type" "" { target *-*-* } 0 }
>
> struct MoveOnly
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
>index 4702932627e..c73319010ee 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
>@@ -1,15 +1,85 @@
>-// { dg-do compile { target c++20 } }
>+// Copyright (C) 2019-2025 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-do run { target c++20 } }
>+// { dg-require-atomic-cmpxchg-word "" }
>+// { dg-add-options libatomic }
>
> #include <atomic>
>+#include <testsuite_hooks.h>
>+
>+void
>+test01()
>+{
>+ bool value;
>+
>+ {
>+ const auto mo = std::memory_order_relaxed;
>+ std::atomic_ref<bool> a(value);
>+ bool ok = a.is_lock_free();
>+ if constexpr (std::atomic_ref<bool>::is_always_lock_free)
>+ VERIFY( ok );
>+ a = false;
>+ VERIFY( !a.load() );
>+ VERIFY( !a.load(mo) );
>+ a.store(true);
>+ VERIFY( a.load() );
>+ auto v = a.exchange(false);
>+ VERIFY( !a.load() );
>+ VERIFY( v );
>+ v = a.exchange(true, mo);
>+ VERIFY( a.load() );
>+ VERIFY( !v );
>+
>+ auto expected = a.load();
>+ while (!a.compare_exchange_weak(expected, false, mo, mo))
>+ { /* weak form can fail spuriously */ }
>+ VERIFY( !a.load() );
>+ VERIFY( expected );
>+
>+ ok = a.compare_exchange_strong(expected, true);
>+ VERIFY( !ok && !a.load() && !expected );
>+
>+ ok = a.compare_exchange_strong(expected, true);
>+ VERIFY( ok && a.load() && !expected );
>+ }
>+}
>+
>+void
>+test02()
>+{
>+ bool b = false;
>+ std::atomic_ref<bool> a0(b);
>+ std::atomic_ref<bool> a1(b);
>+ std::atomic_ref<const bool> a1c(b);
>+ std::atomic_ref<volatile bool> a1v(b);
>+ std::atomic_ref<const volatile bool> a1cv(b);
>+ std::atomic_ref<bool> a2(a0);
>+ b = true;
>+ VERIFY( a1.load() );
>+ VERIFY( a1c.load() );
>+ VERIFY( a1v.load() );
>+ VERIFY( a1cv.load() );
>+ VERIFY( a2.load() );
>+}
>
>-template<class T> concept has_and = requires (T& a) { a &= false; };
>-template<class T> concept has_or = requires (T& a) { a |= false; };
>-template<class T> concept has_xor = requires (T& a) { a ^= false; };
>-template<class T> concept has_fetch_add = requires (T& a) { a.fetch_add(true); };
>-template<class T> concept has_fetch_sub = requires (T& a) { a.fetch_sub(true); };
>-
>-static_assert( not has_and<std::atomic_ref<bool>> );
>-static_assert( not has_or<std::atomic_ref<bool>> );
>-static_assert( not has_xor<std::atomic_ref<bool>> );
>-static_assert( not has_fetch_add<std::atomic_ref<bool>> );
>-static_assert( not has_fetch_sub<std::atomic_ref<bool>> );
>+int
>+main()
>+{
>+ test01();
>+ test02();
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc
>new file mode 100644
>index 00000000000..dfc6a559945
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc
>@@ -0,0 +1,94 @@
>+// Copyright (C) 2019-2025 Free Software Foundation, Inc.
>+//
>+// This file is part of the GNU ISO C++ Library. This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>+// GNU General Public License for more details.
>+
>+// You should have received a copy of the GNU General Public License along
>+// with this library; see the file COPYING3. If not see
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-do run { target c++20 } }
>+// { dg-require-atomic-cmpxchg-word "" }
>+// { dg-add-options libatomic }
>+
>+#include <atomic>
>+#include <testsuite_hooks.h>
>+
>+struct X
>+{
>+ X() = default;
>+ X(int i) : i(i) { }
>+ int i;
>+
>+ friend bool
>+ operator==(X, X) = default;
>+};
>+
>+template<typename V>
>+void
>+test01(V v0, V v1)
>+{
>+ V value;
>+
>+ if constexpr (std::atomic_ref<V>::is_always_lock_free)
>+ {
>+ std::atomic_ref<volatile V> a(value);
>+ VERIFY( a.is_lock_free() );
>+
>+ a = v0;
>+ VERIFY( V(a) == v0 );
>+ VERIFY( a.load() == v0 );
>+
>+ a.store(v1);
>+ VERIFY( a.load() == v1 );
>+
>+ V last = a.exchange(v0);
>+ VERIFY( a.load() == v0 );
>+ VERIFY( last == v1 );
>+
>+ V expected = a.load();
>+ while (!a.compare_exchange_weak(expected, v1))
>+ { /* weak form can fail spuriously */ }
>+ VERIFY( a.load() == v1 );
>+ VERIFY( expected == v0 );
>+
>+ bool ok;
>+ ok = a.compare_exchange_strong(expected, v0);
>+ VERIFY( !ok && a.load() == v1 && expected == v1 );
>+
>+ ok = a.compare_exchange_strong(expected, v0);
>+ VERIFY( ok && a.load() == v0 && expected == v1 );
>+
>+ std::atomic_ref<const volatile V> cva(value);
>+ VERIFY( cva.is_lock_free() );
>+ VERIFY( V(cva) == v0 );
>+ VERIFY( cva.load() == v0 );
>+ }
>+
>+ value = v0;
>+ std::atomic_ref<const V> ca(value);
>+ bool lf = ca.is_lock_free();
>+ if constexpr (std::atomic_ref<V>::is_always_lock_free)
>+ VERIFY( lf );
>+ VERIFY( V(ca) == v0 );
>+ VERIFY( ca.load() == v0 );
>+}
>+
>+int
>+main()
>+{
>+ int x;
>+ test01<bool>(false, true);
>+ test01<int>(1, 2);
>+ test01<float>(1.2, 3.4);
>+ test01<int*>(&x, &x+1);
>+ test01<X>(12, 13);
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc
>index f67190e97a3..01dbfce2375 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc
>@@ -19,22 +19,29 @@
>
> #include <atomic>
>
>+template <typename T>
> void
>-test01()
>+test_impl(T v)
> {
>- int i = 0;
>- std::atomic_ref a0(i);
>- static_assert(std::is_same_v<decltype(a0), std::atomic_ref<int>>);
>-
>- float f = 1.0f;
>- std::atomic_ref a1(f);
>- static_assert(std::is_same_v<decltype(a1), std::atomic_ref<float>>);
>+ std::atomic_ref a(v);
>+ static_assert(std::is_same_v<decltype(a), std::atomic_ref<T>>);
>+}
>
>- int* p = &i;
>- std::atomic_ref a2(p);
>- static_assert(std::is_same_v<decltype(a2), std::atomic_ref<int*>>);
>+template <typename T>
>+void
>+test(T v)
>+{
>+ test_impl<T>(v);
>+ test_impl<const T>(v);
>+ test_impl<volatile T>(v);
>+ test_impl<const volatile T>(v);
>+}
>
>+int main()
>+{
>+ test<int>(0);
>+ test<float>(1.0f);
>+ test<int*>(nullptr);
> struct X { } x;
>- std::atomic_ref a3(x);
>- static_assert(std::is_same_v<decltype(a3), std::atomic_ref<X>>);
>+ test<X>(x);
> }
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc
>index 5773d144c36..c69f3a711d3 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc
>@@ -299,14 +299,19 @@ test04()
> {
> if constexpr (std::atomic_ref<float>::is_always_lock_free)
> {
>- float i = 0;
>- float* ptr = 0;
>- std::atomic_ref<float*> a0(ptr);
>- std::atomic_ref<float*> a1(ptr);
>- std::atomic_ref<float*> a2(a0);
>- a0 = &i;
>- VERIFY( a1 == &i );
>- VERIFY( a2 == &i );
>+ float i = 0.0f;
>+ std::atomic_ref<float> a0(i);
>+ std::atomic_ref<float> a1(i);
>+ std::atomic_ref<const float> a1c(i);
>+ std::atomic_ref<volatile float> a1v(i);
>+ std::atomic_ref<const volatile float> a1cv(i);
>+ std::atomic_ref<float> a2(a0);
>+ a0 = 1.0f;
>+ VERIFY( a1 == 1.0f );
>+ VERIFY( a1c == 1.0f );
>+ VERIFY( a1v == 1.0f );
>+ VERIFY( a1cv == 1.0f );
>+ VERIFY( a2 == 1.0f );
> }
> }
>
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc
>index 2e6fa0f90e2..079ec1b1a78 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc
>@@ -108,9 +108,15 @@ test02()
> X i;
> std::atomic_ref<X> a0(i);
> std::atomic_ref<X> a1(i);
>+ std::atomic_ref<const X> a1c(i);
>+ std::atomic_ref<volatile X> a1v(i);
>+ std::atomic_ref<const volatile X> a1cv(i);
> std::atomic_ref<X> a2(a0);
> a0 = 42;
> VERIFY( a1.load() == 42 );
>+ VERIFY( a1c.load() == 42 );
>+ VERIFY( a1v.load() == 42 );
>+ VERIFY( a1cv.load() == 42 );
> VERIFY( a2.load() == 42 );
> }
>
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
>index f6b68ebc598..310434cefb5 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
>@@ -302,9 +302,15 @@ test03()
> int i = 0;
> std::atomic_ref<int> a0(i);
> std::atomic_ref<int> a1(i);
>+ std::atomic_ref<const int> a1c(i);
>+ std::atomic_ref<volatile int> a1v(i);
>+ std::atomic_ref<const volatile int> a1cv(i);
> std::atomic_ref<int> a2(a0);
> a0 = 42;
> VERIFY( a1 == 42 );
>+ VERIFY( a1c == 42 );
>+ VERIFY( a1v == 42 );
>+ VERIFY( a1cv == 42 );
> VERIFY( a2 == 42 );
> }
>
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc
>new file mode 100644
>index 00000000000..93c65dce263
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc
>@@ -0,0 +1,113 @@
>+// { dg-do compile { target c++20 } }
>+
>+#include <atomic>
>+
>+template<class T> concept has_and = requires (T& a) { a &= false; };
>+template<class T> concept has_or = requires (T& a) { a |= false; };
>+template<class T> concept has_xor = requires (T& a) { a ^= false; };
>+template<class T> concept has_fetch_add = requires (T& a) { a.fetch_add(true); };
>+template<class T> concept has_fetch_sub = requires (T& a) { a.fetch_sub(true); };
>+
>+static constexpr std::memory_order mo = std::memory_order_seq_cst;
>+
>+#define HAS(op) (requires (std::atomic_ref<T> a, T t) { op; })
>+
>+template<typename T>
>+void
>+no_stores()
>+{
>+ static_assert( !HAS(a = t) );
>+ static_assert( !HAS(a.store(t)) );
>+ static_assert( !HAS(a.store(t, mo)) );
>+ static_assert( !HAS(a.exchange(t)) );
>+ static_assert( !HAS(a.exchange(t, mo)) );
>+
>+ static_assert( !HAS(a.compare_exchange_weak(t, t)) );
>+ static_assert( !HAS(a.compare_exchange_weak(t, t, mo)) );
>+ static_assert( !HAS(a.compare_exchange_weak(t, t, mo, mo)) );
>+
>+ static_assert( !HAS(a.compare_exchange_strong(t, t)) );
>+ static_assert( !HAS(a.compare_exchange_strong(t, t, mo)) );
>+ static_assert( !HAS(a.compare_exchange_strong(t, t, mo, mo)) );
>+}
>+
>+template<typename T>
>+void
>+no_additions()
>+{
>+ static_assert( !HAS(a++) );
>+ static_assert( !HAS(++a) );
>+ static_assert( !HAS(a += t) );
>+ static_assert( !HAS(a.fetch_add(t)) );
>+ static_assert( !HAS(a.fetch_add(t, mo)) );
>+
>+ static_assert( !HAS(a--) );
>+ static_assert( !HAS(--a) );
>+ static_assert( !HAS(a -= t) );
>+ static_assert( !HAS(a.fetch_sub(t)) );
>+ static_assert( !HAS(a.fetch_sub(t, mo)) );
>+}
>+
>+template<typename T>
>+void
>+no_bitops()
>+{
>+ static_assert( !HAS(a &= t) );
>+ static_assert( !HAS(a.fetch_and(t)) );
>+ static_assert( !HAS(a.fetch_and(t, mo)) );
>+
>+ static_assert( !HAS(a |= t) );
>+ static_assert( !HAS(a.fetch_or(t)) );
>+ static_assert( !HAS(a.fetch_or(t, mo)) );
>+
>+ static_assert( !HAS(a ^= t) );
>+ static_assert( !HAS(a.fetch_xor(t)) );
>+ static_assert( !HAS(a.fetch_xor(t, mo)) );
>+}
>+
>+template<typename T>
>+void
>+no_math()
>+{
>+ no_additions<T>();
>+ no_bitops<T>();
>+}
>+
>+template<typename T>
>+void
>+no_mutations()
>+{
>+ no_stores<T>();
>+ no_math<T>();
>+}
>+
>+struct S
>+{
>+ int x;
>+ int y;
>+};
>+
>+int main()
>+{
>+ no_mutations<const int>();
>+ no_mutations<const volatile int>();
>+
>+ no_bitops<float>();
>+ no_bitops<volatile float>();
>+ no_mutations<const float>();
>+
>+ no_bitops<int*>();
>+ no_bitops<int* volatile>();
>+ no_mutations<int* const>();
>+ no_mutations<int* const volatile>();
>+
>+ no_math<bool>();
>+ no_math<volatile bool>();
>+ no_mutations<const bool>();
>+ no_mutations<const volatile bool>();
>+
>+ no_math<S>();
>+ no_math<volatile S>();
>+ no_mutations<const S>();
>+ no_mutations<const volatile S>();
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc
>index d1789af890e..8db45c797c8 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc
>@@ -210,9 +210,15 @@ test03()
> int* ptr = 0;
> std::atomic_ref<int*> a0(ptr);
> std::atomic_ref<int*> a1(ptr);
>+ std::atomic_ref<int* const> a1c(ptr);
>+ std::atomic_ref<int* volatile> a1v(ptr);
>+ std::atomic_ref<int* const volatile> a1cv(ptr);
> std::atomic_ref<int*> a2(a0);
> a0 = &i;
> VERIFY( a1 == &i );
>+ VERIFY( a1c == &i );
>+ VERIFY( a1v == &i );
>+ VERIFY( a1cv == &i );
> VERIFY( a2 == &i );
> }
>
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc
>index 3b929563a1e..8617661f8e1 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc
>@@ -18,56 +18,92 @@
> // { dg-do compile { target c++20 } }
>
> #include <atomic>
>+#include <type_traits>
>
>+template <class T>
> void
>-test01()
>+test_generic()
> {
>- struct X { int c; };
>- using A = std::atomic_ref<X>;
>+ using A = std::atomic_ref<T>;
> static_assert( std::is_standard_layout_v<A> );
> static_assert( std::is_nothrow_copy_constructible_v<A> );
> static_assert( std::is_trivially_destructible_v<A> );
>- static_assert( std::is_same_v<A::value_type, X> );
>+ static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> );
>+ static_assert( !requires { typename A::difference_type; } );
> static_assert( !std::is_copy_assignable_v<A> );
> static_assert( !std::is_move_assignable_v<A> );
> }
>
>+template <class T>
> void
>-test02()
>+test_integral()
> {
>- using A = std::atomic_ref<int>;
>+ using A = std::atomic_ref<T>;
> static_assert( std::is_standard_layout_v<A> );
> static_assert( std::is_nothrow_copy_constructible_v<A> );
> static_assert( std::is_trivially_destructible_v<A> );
>- static_assert( std::is_same_v<A::value_type, int> );
>- static_assert( std::is_same_v<A::difference_type, A::value_type> );
>+ static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> );
>+ static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> );
> static_assert( !std::is_copy_assignable_v<A> );
> static_assert( !std::is_move_assignable_v<A> );
> }
>
>+template <class T>
> void
>-test03()
>+test_floating_point()
> {
>- using A = std::atomic_ref<double>;
>+ using A = std::atomic_ref<T>;
> static_assert( std::is_standard_layout_v<A> );
> static_assert( std::is_nothrow_copy_constructible_v<A> );
> static_assert( std::is_trivially_destructible_v<A> );
>- static_assert( std::is_same_v<A::value_type, double> );
>- static_assert( std::is_same_v<A::difference_type, A::value_type> );
>+ static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> );
>+ static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> );
> static_assert( !std::is_copy_assignable_v<A> );
> static_assert( !std::is_move_assignable_v<A> );
> }
>
>+template <class T>
> void
>-test04()
>+test_pointer()
> {
>- using A = std::atomic_ref<int*>;
>+ using A = std::atomic_ref<T>;
> static_assert( std::is_standard_layout_v<A> );
> static_assert( std::is_nothrow_copy_constructible_v<A> );
> static_assert( std::is_trivially_destructible_v<A> );
>- static_assert( std::is_same_v<A::value_type, int*> );
>- static_assert( std::is_same_v<A::difference_type, std::ptrdiff_t> );
>+ static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> );
>+ static_assert( std::is_same_v<typename A::difference_type, std::ptrdiff_t> );
> static_assert( std::is_nothrow_copy_constructible_v<A> );
> static_assert( !std::is_copy_assignable_v<A> );
> static_assert( !std::is_move_assignable_v<A> );
> }
>+
>+int
>+main()
>+{
>+ struct X { int c; };
>+ test_generic<X>();
>+ test_generic<const X>();
>+ test_generic<volatile X>();
>+ test_generic<const volatile X>();
>+
>+ // atomic_ref excludes (cv) `bool` from the set of integral types
>+ test_generic<bool>();
>+ test_generic<const bool>();
>+ test_generic<volatile bool>();
>+ test_generic<const volatile bool>();
>+
>+ test_integral<int>();
>+ test_integral<const int>();
>+ test_integral<volatile int>();
>+ test_integral<const volatile int>();
>+
>+ test_floating_point<double>();
>+ test_floating_point<const double>();
>+ test_floating_point<volatile double>();
>+ test_floating_point<const volatile double>();
>+
>+ test_pointer<int*>();
>+ test_pointer<int* const>();
>+ test_pointer<int* volatile>();
>+ test_pointer<int* const volatile>();
>+}
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc
>new file mode 100644
>index 00000000000..8b0abbde023
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc
>@@ -0,0 +1,34 @@
>+// { dg-do compile { target c++20 } }
>+
>+#include <atomic>
>+
>+template<size_t N>
>+struct NonTrivial
>+{
>+ NonTrivial() = default;
>+ NonTrivial(NonTrivial const&) { };
>+};
>+
>+template<size_t N>
>+NonTrivial<N> ntv;
>+
>+std::atomic_ref<NonTrivial<0>> nt(ntv<0>); // { dg-error "here" }
>+std::atomic_ref<const NonTrivial<1>> cnt(ntv<1>); // { dg-error "here" }
>+std::atomic_ref<volatile NonTrivial<2>> vnt(ntv<2>); // { dg-error "here" }
>+std::atomic_ref<const volatile NonTrivial<3>> cvnt(ntv<3>); // { dg-error "here" }
>+
>+template<size_t N>
>+struct NonLockFree
>+{
>+ char c[1024 + N];
>+};
>+
>+template<size_t N>
>+NonLockFree<N> nlfv;
>+
>+std::atomic_ref<NonLockFree<0>> nlf(nlfv<0>);
>+std::atomic_ref<const NonLockFree<1>> cnlf(nlfv<1>);
>+std::atomic_ref<volatile NonLockFree<2>> vnlf(nlfv<2>); // { dg-error "here" }
>+std::atomic_ref<const volatile NonLockFree<3>> cvnlf(nlfv<3>); // { dg-error "here" }
>+
>+// { dg-error "static assertion failed" "" { target *-*-* } 0 }
>diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc
>index ecabeecd5bb..db20a197ed0 100644
>--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc
>+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc
>@@ -41,6 +41,16 @@ template<typename S>
> });
> a.wait(va);
> t.join();
>+
>+ std::atomic_ref<const S> b{ aa };
>+ b.wait(va);
>+ std::thread t2([&]
>+ {
>+ a.store(va);
>+ a.notify_one();
>+ });
>+ b.wait(vb);
>+ t2.join();
> }
> }
>
>--
>2.51.0
>
>
More information about the Libstdc++
mailing list