Hello,

The attached patch implements P3323R1, a DR against C++20 (11?), which fixes LWG 4069 and 3508 by clarifying that std::atomic_ref<cv T> is meant to be supported (whereas std::atomic<cv T> is meant to be unsupported).

I've tried to keep the refactorings to a minimum and follow the structure of the existing code, where atomic_ref<T> dispatched to a __atomic_ref<T> base class which had specializations for various kinds of T (integral, fp, pointer). The mutating operations on an atomic_ref<T> are now constrained on whether T is const or not, so I split __atomic_ref into a further subclass (__atomic_ref_base) that contains the non-mutating operations. __atomic_ref inherits from _base and adds the mutating operations if T is non const.

Thanks,
--
Giuseppe D'Angelo
From c75e170548dd40c58940cb18d05f413c76d21d34 Mon Sep 17 00:00:00 2001
From: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Date: Mon, 9 Dec 2024 01:32:27 +0100
Subject: [PATCH] libstdc++: add support for cv-qualified types in atomic_ref
 (P3323R1)

P3233R1 (DR for C++20/C++11, fixes LWG 4069 and 3508) clarifies that
std::atomic_ref<cv T> is meant to be supported.

This commit implements it by splitting the __atomic_ref class (that
atomic_ref inherits from) into a further base class (__atomic_ref_base):

* __atomic_ref_base<T> implements the atomic API for const and non-const
  Ts (with specializations for integrals, floating points, pointers);
* __atomic_ref<T> inherits from __atomic_ref_base<T>; if T is
  non-const adds on top the "mutating" atomic APIs like store(),
  exchange(), and so on; same discussion w.r.t. the specializations.

The primary atomic_ref is now meant to be used for cv-bool, not just
bool, amend the detection accordingly.

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.

libstdc++-v3/ChangeLog:

	* include/bits/atomic_base.h: Add support for atomic_ref<cv T>:
	  refactor __atomic_ref into a further subclass in order to
	  implement the constraints on atomic_ref mutating APIs; change
          _Tp in various function signatures to be value_type instead.
	* include/std/atomic: Add a static_assert to std::atomic, as per
	  P3233R1, complementing the existing ones.
	* testsuite/29_atomics/atomic_ref/bool.cc: Add tests for
	  cv types in atomic_ref.
	* testsuite/29_atomics/atomic_ref/deduction.cc: Likewise.
	* 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: Likewise.

Signed-off-by: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
---
 libstdc++-v3/include/bits/atomic_base.h       | 507 +++++++++++-------
 libstdc++-v3/include/std/atomic               |   1 +
 .../testsuite/29_atomics/atomic_ref/bool.cc   |  18 +
 .../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/pointer.cc          |   6 +
 .../29_atomics/atomic_ref/requirements.cc     |  70 ++-
 .../29_atomics/atomic_ref/wait_notify.cc      |  10 +
 10 files changed, 440 insertions(+), 238 deletions(-)

diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 72cc4bae6cf..df642716ce8 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -1473,14 +1473,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 #undef _GLIBCXX20_INIT
 
+  // atomic_ref inherits from __atomic_ref;
+  // __atomic_ref inherits from __atomic_ref_base.
+  //
+  // __atomic_ref_base provides the common APIs for const and non-const types;
+  // __atomic ref adds on top the APIs for non-const types, thus implementing
+  // the various constraints in [atomic.ref].
+
   template<typename _Tp,
-           bool = is_integral_v<_Tp> && !is_same_v<_Tp, bool>,
-           bool = is_floating_point_v<_Tp>>
+           bool = is_const_v<_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,
+           bool _IsIntegral,
+           bool _IsFloatingPoint,
+           bool _IsPointer>
+    struct __atomic_ref_base;
+
+  // Const types
+  template<typename _Tp, bool _IsIntegral, bool _IsFloatingPoint, bool _IsPointer>
+    struct __atomic_ref<_Tp, true, _IsIntegral, _IsFloatingPoint, _IsPointer>
+      : __atomic_ref_base<_Tp, _IsIntegral, _IsFloatingPoint, _IsPointer>
+    {
+      __atomic_ref() = delete;
+      __atomic_ref& operator=(const __atomic_ref&) = delete;
+
+      explicit
+      __atomic_ref(_Tp& __t) : __atomic_ref_base<_Tp, _IsIntegral, _IsFloatingPoint, _IsPointer>(__t)
+      { }
+    };
+
+  // Non-integral, non-floating-point, non-pointer types
   template<typename _Tp>
-    struct __atomic_ref<_Tp, false, false>
+    struct __atomic_ref_base<_Tp, false, false, false>
     {
       static_assert(is_trivially_copyable_v<_Tp>);
 
@@ -1490,70 +1518,97 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	? 0 : sizeof(_Tp);
 
     public:
-      using value_type = _Tp;
+      using value_type = remove_cv_t<_Tp>;
 
       static constexpr bool is_always_lock_free
 	= __atomic_always_lock_free(sizeof(_Tp), 0);
 
+      static_assert(is_always_lock_free || !is_volatile_v<_Tp>);
+
       static constexpr size_t required_alignment
 	= _S_min_alignment > alignof(_Tp) ? _S_min_alignment : alignof(_Tp);
 
-      __atomic_ref& operator=(const __atomic_ref&) = delete;
+      __atomic_ref_base& operator=(const __atomic_ref_base&) = delete;
 
       explicit
-      __atomic_ref(_Tp& __t) : _M_ptr(std::__addressof(__t))
+      __atomic_ref_base(_Tp& __t) : _M_ptr(std::__addressof(__t))
       {
 	__glibcxx_assert(((__UINTPTR_TYPE__)_M_ptr % required_alignment) == 0);
       }
 
-      __atomic_ref(const __atomic_ref&) noexcept = default;
+      __atomic_ref_base(const __atomic_ref_base&) noexcept = default;
 
-      _Tp
-      operator=(_Tp __t) const noexcept
-      {
-	this->store(__t);
-	return __t;
-      }
-
-      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)
+#if __glibcxx_atomic_wait
+      _GLIBCXX_ALWAYS_INLINE void
+      wait(value_type __old, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::wait(_M_ptr, __old, __m); }
+
+      // TODO add const volatile overload
+#endif // __glibcxx_atomic_wait
+
+    protected:
+      _Tp* _M_ptr;
+    };
+
+  template<typename _Tp>
+    struct __atomic_ref<_Tp, false, false, false, false>
+      : __atomic_ref_base<_Tp, false, false, false>
+    {
+      using value_type = typename __atomic_ref_base<_Tp, false, false, false>::value_type;
+
+      __atomic_ref() = delete;
+      __atomic_ref& operator=(const __atomic_ref&) = delete;
+
+      explicit
+      __atomic_ref(_Tp& __t) : __atomic_ref_base<_Tp, false, false, false>(__t)
+      { }
+
+      void
+      store(value_type __t, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::store(this->_M_ptr, __t, __m); }
+
+      value_type
+      operator=(value_type __t) const noexcept
+      {
+	this->store(__t);
+	return __t;
+      }
+
+      value_type
+      exchange(value_type __desired, memory_order __m = memory_order_seq_cst)
       const noexcept
-      { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
+      { 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,
+      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
       {
@@ -1562,7 +1617,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       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
       {
@@ -1571,64 +1626,51 @@ _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); }
+      { __atomic_impl::notify_one(this->_M_ptr); }
 
       // TODO add const volatile overload
 
       _GLIBCXX_ALWAYS_INLINE void
       notify_all() const noexcept
-      { __atomic_impl::notify_all(_M_ptr); }
+      { __atomic_impl::notify_all(this->_M_ptr); }
 
       // TODO add const volatile overload
 #endif // __glibcxx_atomic_wait
-
-    private:
-      _Tp* _M_ptr;
     };
 
-  // base class for atomic_ref<integral-type>
+
+  // Integral types (except cv-bool)
   template<typename _Tp>
-    struct __atomic_ref<_Tp, true, false>
+    struct __atomic_ref_base<_Tp, true, false, false>
     {
       static_assert(is_integral_v<_Tp>);
 
     public:
-      using value_type = _Tp;
+      using value_type = remove_cv_t<_Tp>;
       using difference_type = value_type;
 
       static constexpr bool is_always_lock_free
 	= __atomic_always_lock_free(sizeof(_Tp), 0);
 
+      static_assert(is_always_lock_free || !is_volatile_v<_Tp>);
+
       static constexpr size_t required_alignment
 	= sizeof(_Tp) > alignof(_Tp) ? sizeof(_Tp) : alignof(_Tp);
 
-      __atomic_ref() = delete;
-      __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(&__t)
+      __atomic_ref_base(_Tp& __t) : _M_ptr(&__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
@@ -1636,39 +1678,71 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	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,
+#if __glibcxx_atomic_wait
+      _GLIBCXX_ALWAYS_INLINE void
+      wait(value_type __old, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::wait(_M_ptr, __old, __m); }
+
+      // TODO add const volatile overload
+#endif // __glibcxx_atomic_wait
+
+    protected:
+      _Tp* _M_ptr;
+    };
+
+  template<typename _Tp>
+    struct __atomic_ref<_Tp, false, true, false, false>
+      : __atomic_ref_base<_Tp, true, false, false>
+    {
+      using value_type = typename __atomic_ref_base<_Tp, true, false, false>::value_type;
+
+      __atomic_ref() = delete;
+      __atomic_ref& operator=(const __atomic_ref&) = delete;
+
+      explicit
+      __atomic_ref(_Tp& __t) : __atomic_ref_base<_Tp, true, false, false>(__t)
+      { }
+
+      value_type
+      operator=(value_type __t) const noexcept
+      {
+	this->store(__t);
+	return __t;
+      }
+
+      void
+      store(value_type __t, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::store(this->_M_ptr, __t, __m); }
+
+      value_type
+      exchange(value_type __desired,
 	       memory_order __m = memory_order_seq_cst) const noexcept
-      { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
+      { 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,
+      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
       {
@@ -1677,7 +1751,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       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
       {
@@ -1686,21 +1760,15 @@ _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); }
+      { __atomic_impl::notify_one(this->_M_ptr); }
 
       // TODO add const volatile overload
 
       _GLIBCXX_ALWAYS_INLINE void
       notify_all() const noexcept
-      { __atomic_impl::notify_all(_M_ptr); }
+      { __atomic_impl::notify_all(this->_M_ptr); }
 
       // TODO add const volatile overload
 #endif // __glibcxx_atomic_wait
@@ -1708,27 +1776,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       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
@@ -1740,70 +1808,62 @@ _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); }
-
-    private:
-      _Tp* _M_ptr;
+      { return __atomic_impl::__xor_fetch(this->_M_ptr, __i); }
     };
 
-  // base class for atomic_ref<floating-point-type>
+  // Floating-point types
   template<typename _Fp>
-    struct __atomic_ref<_Fp, false, true>
+    struct __atomic_ref_base<_Fp, false, true, false>
     {
       static_assert(is_floating_point_v<_Fp>);
 
     public:
-      using value_type = _Fp;
+      using value_type = remove_cv_t<_Fp>;
       using difference_type = value_type;
 
       static constexpr bool is_always_lock_free
 	= __atomic_always_lock_free(sizeof(_Fp), 0);
 
+      static_assert(is_always_lock_free || !is_volatile_v<_Fp>);
+
       static constexpr size_t required_alignment = __alignof__(_Fp);
 
-      __atomic_ref() = delete;
-      __atomic_ref& operator=(const __atomic_ref&) = delete;
+      __atomic_ref_base() = delete;
+      __atomic_ref_base& operator=(const __atomic_ref_base&) = delete;
 
       explicit
-      __atomic_ref(_Fp& __t) : _M_ptr(&__t)
+      __atomic_ref_base(_Fp& __t) : _M_ptr(&__t)
       {
 	__glibcxx_assert(((__UINTPTR_TYPE__)_M_ptr % required_alignment) == 0);
       }
 
-      __atomic_ref(const __atomic_ref&) noexcept = default;
+      __atomic_ref_base(const __atomic_ref_base&) noexcept = default;
 
-      _Fp
-      operator=(_Fp __t) const noexcept
-      {
-	this->store(__t);
-	return __t;
-      }
-
-      operator _Fp() const noexcept { return this->load(); }
+      operator value_type() const noexcept { return this->load(); }
 
       bool
       is_lock_free() const noexcept
@@ -1811,39 +1871,71 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	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); }
 
+#if __glibcxx_atomic_wait
+      _GLIBCXX_ALWAYS_INLINE void
+      wait(value_type __old, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::wait(_M_ptr, __old, __m); }
+
+      // TODO add const volatile overload
+#endif // __glibcxx_atomic_wait
+
+    protected:
+      _Fp* _M_ptr;
+    };
+
+  template<typename _Fp>
+    struct __atomic_ref<_Fp, false, false, true, false>
+      : __atomic_ref_base<_Fp, false, true, false>
+    {
+      using value_type = typename __atomic_ref_base<_Fp, false, true, false>::value_type;
+
+      __atomic_ref() = delete;
+      __atomic_ref& operator=(const __atomic_ref&) = delete;
+
+      explicit
+      __atomic_ref(_Fp& __t) : __atomic_ref_base<_Fp, false, true, false>(__t)
+      { }
+
+      value_type
+      operator=(value_type __t) const noexcept
+      {
+	this->store(__t);
+	return __t;
+      }
+
+      void
+      store(value_type __t, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::store(this->_M_ptr, __t, __m); }
+
       _Fp
-      exchange(_Fp __desired,
+      exchange(value_type __desired,
 	       memory_order __m = memory_order_seq_cst) const noexcept
-      { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
+      { return __atomic_impl::exchange(this->_M_ptr, __desired, __m); }
 
       bool
-      compare_exchange_weak(_Fp& __expected, _Fp __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(_Fp& __expected, _Fp __desired,
+      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(_Fp& __expected, _Fp __desired,
+      compare_exchange_weak(value_type& __expected, value_type __desired,
 			    memory_order __order = memory_order_seq_cst)
       const noexcept
       {
@@ -1852,7 +1944,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       bool
-      compare_exchange_strong(_Fp& __expected, _Fp __desired,
+      compare_exchange_strong(value_type& __expected, value_type __desired,
 			      memory_order __order = memory_order_seq_cst)
       const noexcept
       {
@@ -1861,21 +1953,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
 #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); }
+      { __atomic_impl::notify_one(this->_M_ptr); }
 
       // TODO add const volatile overload
 
       _GLIBCXX_ALWAYS_INLINE void
       notify_all() const noexcept
-      { __atomic_impl::notify_all(_M_ptr); }
+      { __atomic_impl::notify_all(this->_M_ptr); }
 
       // TODO add const volatile overload
 #endif // __glibcxx_atomic_wait
@@ -1883,56 +1969,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       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); }
-
-    private:
-      _Fp* _M_ptr;
+      { return __atomic_impl::__sub_fetch_flt(this->_M_ptr, __i); }
     };
 
-  // base class for atomic_ref<pointer-type>
+  // Pointer types
   template<typename _Tp>
-    struct __atomic_ref<_Tp*, false, false>
+    struct __atomic_ref_base<_Tp, false, false, true>
     {
+      static_assert(is_pointer_v<_Tp>);
+
     public:
-      using value_type = _Tp*;
+      using value_type = remove_cv_t<_Tp>;
       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*);
+      static_assert(is_always_lock_free || !is_volatile_v<_Tp>);
 
-      __atomic_ref() = delete;
-      __atomic_ref& operator=(const __atomic_ref&) = delete;
+      static constexpr size_t required_alignment = __alignof__(_Tp);
+
+      __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(_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;
-      }
+      __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
@@ -1940,39 +2020,94 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	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,
+#if __glibcxx_atomic_wait
+      _GLIBCXX_ALWAYS_INLINE void
+      wait(value_type __old, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::wait(_M_ptr, __old, __m); }
+
+      // TODO add const volatile overload
+#endif // __glibcxx_atomic_wait
+
+    protected:
+      static constexpr ptrdiff_t
+      _S_type_size(ptrdiff_t __d) noexcept
+      {
+	using _PointedType = remove_pointer_t<_Tp>;
+	static_assert(is_object_v<_PointedType>);
+	return __d * sizeof(_PointedType);
+      }
+
+      _Tp* _M_ptr;
+    };
+
+  template<typename _Tp>
+    struct __atomic_ref<_Tp, false, false, false, true>
+      : __atomic_ref_base<_Tp, false, false, true>
+    {
+      using value_type = typename __atomic_ref_base<_Tp, false, false, true>::value_type;
+      using difference_type = typename __atomic_ref_base<_Tp, false, false, true>::difference_type;
+
+      __atomic_ref() = delete;
+      __atomic_ref& operator=(const __atomic_ref&) = delete;
+
+      explicit
+      __atomic_ref(_Tp& __t) : __atomic_ref_base<_Tp, false, false, true>(__t)
+      { }
+
+#if __glibcxx_atomic_wait
+      _GLIBCXX_ALWAYS_INLINE void
+      notify_one() const noexcept
+      { __atomic_impl::notify_one(this->_M_ptr); }
+
+      // TODO add const volatile overload
+
+      _GLIBCXX_ALWAYS_INLINE void
+      notify_all() const noexcept
+      { __atomic_impl::notify_all(this->_M_ptr); }
+
+      // TODO add const volatile overload
+#endif // __glibcxx_atomic_wait
+
+      value_type
+      operator=(value_type __t) const noexcept
+      {
+	this->store(__t);
+	return __t;
+      }
+
+      void
+      store(value_type __t, memory_order __m = memory_order_seq_cst) const noexcept
+      { __atomic_impl::store(this->_M_ptr, __t, __m); }
+
+      value_type
+      exchange(value_type __desired,
 	       memory_order __m = memory_order_seq_cst) const noexcept
-      { return __atomic_impl::exchange(_M_ptr, __desired, __m); }
+      { 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,
+      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
       {
@@ -1981,7 +2116,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
       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
       {
@@ -1989,35 +2124,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 				       __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
-
       _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, this->_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, this->_S_type_size(__d), __m); }
 
       value_type
       operator++(int) const noexcept
@@ -2030,36 +2145,26 @@ _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, this->_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, this->_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, this->_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, this->_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);
-      }
-
-      _Tp** _M_ptr;
     };
 #endif // C++2a
 
diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
index 503dca945d3..0b83643fca4 100644
--- a/libstdc++-v3/include/std/atomic
+++ b/libstdc++-v3/include/std/atomic
@@ -222,6 +222,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       static_assert(is_move_constructible_v<_Tp>);
       static_assert(is_copy_assignable_v<_Tp>);
       static_assert(is_move_assignable_v<_Tp>);
+      static_assert(is_same_v<_Tp, remove_cv_t<_Tp>>);
 #endif
 
     public:
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
index 4702932627e..7b362737afb 100644
--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
@@ -13,3 +13,21 @@ 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>> );
+
+static_assert( not has_and<std::atomic_ref<const bool>> );
+static_assert( not has_or<std::atomic_ref<const bool>> );
+static_assert( not has_xor<std::atomic_ref<const bool>> );
+static_assert( not has_fetch_add<std::atomic_ref<const bool>> );
+static_assert( not has_fetch_sub<std::atomic_ref<const bool>> );
+
+static_assert( not has_and<std::atomic_ref<volatile bool>> );
+static_assert( not has_or<std::atomic_ref<volatile bool>> );
+static_assert( not has_xor<std::atomic_ref<volatile bool>> );
+static_assert( not has_fetch_add<std::atomic_ref<volatile bool>> );
+static_assert( not has_fetch_sub<std::atomic_ref<volatile bool>> );
+
+static_assert( not has_and<std::atomic_ref<const volatile bool>> );
+static_assert( not has_or<std::atomic_ref<const volatile bool>> );
+static_assert( not has_xor<std::atomic_ref<const volatile bool>> );
+static_assert( not has_fetch_add<std::atomic_ref<const volatile bool>> );
+static_assert( not has_fetch_sub<std::atomic_ref<const volatile bool>> );
diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc
index 642c867e33b..f8fc03e3426 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 fe2fe238128..8d192989272 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 5d989e3bb68..b2041c1e555 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 e5e6364dad9..5b7ee8bc15a 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/pointer.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc
index cd75f9821ea..ba251c2974f 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 96d003f99b4..6c318e66f58 100644
--- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc
@@ -18,56 +18,94 @@
 // { 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( !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>;
+  static_assert( std::is_integral_v<T> );
+  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>;
+  static_assert( std::is_floating_point_v<T> );
+  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*>;
+  static_assert( std::is_pointer_v<T> );
+  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>();
+}
\ No newline at end of file
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 503d20d0044..922af652d2c 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.34.1

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to