On Fri, 26 Sep 2025 at 11:17 +0200, Tomasz Kamiński wrote:
From: Giuseppe D'Angelo <[email protected]>

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 <[email protected]>
Signed-off-by: Giuseppe D'Angelo <[email protected]>
Signed-off-by: Tomasz Kamiński <[email protected]>
---
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



Reply via email to