https://gcc.gnu.org/g:90dde804626f1344f0d63827709406b135433272

commit r16-4349-g90dde804626f1344f0d63827709406b135433272
Author: Giuseppe D'Angelo <[email protected]>
Date:   Mon Dec 9 01:32:27 2024 +0100

    libstdc++: Handle cv-qualified types in atomic and atomic_ref [PR115402]
    
    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  extractingparts of the __atomic_ref class (that atomic_ref 
inherits
    from) into a further base class (__atomic_ref_base):
    
    * __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.
    
    Reviewed-by: Jonathan Wakely <[email protected]>
    Co-authored-by: Tomasz Kamiński <[email protected]>
    Signed-off-by: Giuseppe D'Angelo <[email protected]>
    Signed-off-by: Tomasz Kamiński <[email protected]>

Diff:
---
 libstdc++-v3/include/bits/atomic_base.h            | 566 +++++++--------------
 libstdc++-v3/include/std/atomic                    |   5 +
 .../29_atomics/atomic/requirements/types_neg.cc    |   4 +-
 .../testsuite/29_atomics/atomic_ref/bool.cc        |  94 +++-
 .../testsuite/29_atomics/atomic_ref/cv_qual.cc     |  94 ++++
 .../testsuite/29_atomics/atomic_ref/deduction.cc   |  33 +-
 .../testsuite/29_atomics/atomic_ref/float.cc       |  21 +-
 .../testsuite/29_atomics/atomic_ref/generic.cc     |   6 +
 .../testsuite/29_atomics/atomic_ref/integral.cc    |   6 +
 .../testsuite/29_atomics/atomic_ref/op_support.cc  | 113 ++++
 .../testsuite/29_atomics/atomic_ref/pointer.cc     |   6 +
 .../29_atomics/atomic_ref/requirements.cc          |  68 ++-
 .../29_atomics/atomic_ref/requirements_neg.cc      |  34 ++
 .../testsuite/29_atomics/atomic_ref/wait_notify.cc |  10 +
 14 files changed, 621 insertions(+), 439 deletions(-)

diff --git a/libstdc++-v3/include/bits/atomic_base.h 
b/libstdc++-v3/include/bits/atomic_base.h
index 92d1269493f7..84661d449e25 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
+  // non-const types,
+  // __atomic_ref_base<_Tp> inherits from  __atomic_ref_base<const _Tp>,
+  // and provides the common APIs implementing constraints in [atomic.ref].
+  // __atomic_ref<_Tp> inherits from __atomic_ref_base<_Tp> (const or 
non-const)
+  // adds type-specific mutating APIs.
+  // 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 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)))
       {
        __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 waits on volatile are not 
supported");
+       __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 waits 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 waits 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 9b1aca0fc09a..ccb77fa6327d 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,
+                   "template argument for std::atomic must not be const or 
volatile");
+
 #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 cfe44d255ca2..b9105481006f 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 4702932627e8..c73319010ee8 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 000000000000..dfc6a5599451
--- /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 f67190e97a3a..01dbfce23751 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 5773d144c36a..c69f3a711d34 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 2e6fa0f90e2d..079ec1b1a785 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 f6b68ebc5989..310434cefb54 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 000000000000..93c65dce2636
--- /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 d1789af890eb..8db45c797c8d 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 3b929563a1e7..8617661f8e13 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 000000000000..8b0abbde023f
--- /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 ecabeecd5bb3..db20a197ed06 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();
     }
   }

Reply via email to