]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Handle cv-qualified types in atomic and atomic_ref [PR115402]
authorGiuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Mon, 9 Dec 2024 00:32:27 +0000 (01:32 +0100)
committerTomasz Kamiński <tkaminsk@redhat.com>
Fri, 10 Oct 2025 05:38:17 +0000 (07:38 +0200)
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 <jwakely@redhat.com>
Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com>
Signed-off-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
14 files changed:
libstdc++-v3/include/bits/atomic_base.h
libstdc++-v3/include/std/atomic
libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc [new file with mode: 0644]
libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc [new file with mode: 0644]
libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc
libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc [new file with mode: 0644]
libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc

index 92d1269493f716255742ba8f2839b4f014fecc19..84661d449e25c390571e76ab9647c0c09f58d4fe 100644 (file)
@@ -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
 
index 9b1aca0fc09ab68dc2723ef175782a85128cd365..ccb77fa6327df8982a6ba94a981684b63e09833f 100644 (file)
@@ -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>);
index cfe44d255ca29037df4443938288fee26e7fca0f..b9105481006fb123be4f1a9921eca1681e0c0dfe 100644 (file)
@@ -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
index 4702932627e8c32eebf4bdc81e55a5f83b06fe98..c73319010ee8ef76f2f2046ec11fca20fa671419 100644 (file)
@@ -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 (file)
index 0000000..dfc6a55
--- /dev/null
@@ -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);
+}
index f67190e97a3adf9f801c687540b0c19ab706270b..01dbfce2375180f716ad0c276cf58316a0b0ce21 100644 (file)
 
 #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);
 }
index 5773d144c36a07e70d421b8d2f9e40f9327d325c..c69f3a711d3483974ff64e0b21412994b3993a54 100644 (file)
@@ -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 );
   }
 }
 
index 2e6fa0f90e2d55b8c64faa23fc388878d7e6a342..079ec1b1a785a90eba2133184efc131c3c25d6d2 100644 (file)
@@ -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 );
 }
 
index f6b68ebc598904b22c09cd0b76039f3bd990a58b..310434cefb54a667704cc6a2917918400f3d120f 100644 (file)
@@ -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 (file)
index 0000000..93c65dc
--- /dev/null
@@ -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>();
+}
index d1789af890eb02db87d54519efe5c6689620a5d5..8db45c797c8d2635381d8ba40102912a5538195b 100644 (file)
@@ -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 );
 }
 
index 3b929563a1e7664b10fd058ba426b7ce4f3eaad0..8617661f8e139002bd931ebe962c60ca1a845977 100644 (file)
 // { 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 (file)
index 0000000..8b0abbd
--- /dev/null
@@ -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 }
index ecabeecd5bb3418132dd6be3e10b53ee8fa20d26..db20a197ed069d215720530955049c5c33228061 100644 (file)
@@ -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();
     }
   }