[libcxx-commits] [libcxx] [libc++] cv-qualified types in atomic and atomic_ref (P3323R1) (PR #121414)

via libcxx-commits libcxx-commits at lists.llvm.org
Tue Dec 31 13:10:33 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Damien L-G (dalg24)

<details>
<summary>Changes</summary>

Implement P3323R1 as a DR against C++11(?), respectively C++20, for `std::atomic` and `std::atomic_ref`.

The `std::atomic` changes are straightforward, adding a couple static assertions checking that the class template is only instantiated for cv-unqualified types, along with a test adapted from `libcxx/test/std/atomics/atomics.types.generic/trivially_copyable.verify.cpp`.

The `std::atomic_ref` changes are more involved.  I started converting a subset of the tests to add coverage for volatile- and const-qualified types but I would like feedback before I process the rest.

---

Patch is 38.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/121414.diff


11 Files Affected:

- (modified) libcxx/docs/Status/Cxx2cPapers.csv (+1-1) 
- (modified) libcxx/include/__atomic/atomic_ref.h (+212-64) 
- (modified) libcxx/include/__atomic/support.h (+4) 
- (modified) libcxx/include/__cxx03/__atomic/cxx_atomic_impl.h (+4) 
- (modified) libcxx/test/std/atomics/atomics.ref/assign.pass.cpp (+29-13) 
- (modified) libcxx/test/std/atomics/atomics.ref/convert.pass.cpp (+27-7) 
- (modified) libcxx/test/std/atomics/atomics.ref/deduction.pass.cpp (+11-2) 
- (modified) libcxx/test/std/atomics/atomics.ref/load.pass.cpp (+27-8) 
- (modified) libcxx/test/std/atomics/atomics.ref/store.pass.cpp (+32-8) 
- (modified) libcxx/test/std/atomics/atomics.ref/test_helper.h (+20-19) 
- (added) libcxx/test/std/atomics/atomics.types.generic/cv_unqualified.verify.cpp (+25) 


``````````diff
diff --git a/libcxx/docs/Status/Cxx2cPapers.csv b/libcxx/docs/Status/Cxx2cPapers.csv
index 3a8a794ca4ea1e..990c65a4993a3a 100644
--- a/libcxx/docs/Status/Cxx2cPapers.csv
+++ b/libcxx/docs/Status/Cxx2cPapers.csv
@@ -87,7 +87,7 @@
 "`P3050R2 <https://wg21.link/P3050R2>`__","Fix C++26 by optimizing ``linalg::conjugated`` for noncomplex value types","2024-11 (Wrocław)","","",""
 "`P3396R1 <https://wg21.link/P3396R1>`__","``std::execution`` wording fixes","2024-11 (Wrocław)","","",""
 "`P2835R7 <https://wg21.link/P2835R7>`__","Expose ``std::atomic_ref``'s object address","2024-11 (Wrocław)","","",""
-"`P3323R1 <https://wg21.link/P3323R1>`__","cv-qualified types in ``atomic`` and ``atomic_ref``","2024-11 (Wrocław)","","",""
+"`P3323R1 <https://wg21.link/P3323R1>`__","cv-qualified types in ``atomic`` and ``atomic_ref``","2024-11 (Wrocław)","|Complete|","20","Implemented as DR against C++20."
 "`P3508R0 <https://wg21.link/P3508R0>`__","Wording for ""constexpr for specialized memory algorithms""","2024-11 (Wrocław)","","",""
 "`P3369R0 <https://wg21.link/P3369R0>`__","constexpr for ``uninitialized_default_construct``","2024-11 (Wrocław)","","",""
 "`P3370R1 <https://wg21.link/P3370R1>`__","Add new library headers from C23","2024-11 (Wrocław)","","",""
diff --git a/libcxx/include/__atomic/atomic_ref.h b/libcxx/include/__atomic/atomic_ref.h
index eef15983b98331..f7ed626fed75da 100644
--- a/libcxx/include/__atomic/atomic_ref.h
+++ b/libcxx/include/__atomic/atomic_ref.h
@@ -29,7 +29,12 @@
 #include <__cstddef/ptrdiff_t.h>
 #include <__memory/addressof.h>
 #include <__type_traits/has_unique_object_representation.h>
+#include <__type_traits/is_const.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_trivially_copyable.h>
+#include <__type_traits/is_volatile.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/remove_pointer.h>
 #include <cstdint>
 #include <cstring>
 
@@ -110,7 +115,7 @@ struct __atomic_ref_base {
   static constexpr size_t __min_alignment = (sizeof(_Tp) & (sizeof(_Tp) - 1)) || (sizeof(_Tp) > 16) ? 0 : sizeof(_Tp);
 
 public:
-  using value_type = _Tp;
+  using value_type = __remove_cv_t<_Tp>;
 
   static constexpr size_t required_alignment = alignof(_Tp) > __min_alignment ? alignof(_Tp) : __min_alignment;
 
@@ -123,43 +128,50 @@ struct __atomic_ref_base {
 
   _LIBCPP_HIDE_FROM_ABI bool is_lock_free() const noexcept { return __atomic_is_lock_free(sizeof(_Tp), __ptr_); }
 
-  _LIBCPP_HIDE_FROM_ABI void store(_Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept
-      _LIBCPP_CHECK_STORE_MEMORY_ORDER(__order) {
+  _LIBCPP_HIDE_FROM_ABI void store(value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  _LIBCPP_CHECK_STORE_MEMORY_ORDER(__order) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
         __order == memory_order::relaxed || __order == memory_order::release || __order == memory_order::seq_cst,
         "atomic_ref: memory order argument to atomic store operation is invalid");
     __atomic_store(__ptr_, __clear_padding(__desired), std::__to_gcc_order(__order));
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
     store(__desired);
     return __desired;
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Tp load(memory_order __order = memory_order::seq_cst) const noexcept
+  _LIBCPP_HIDE_FROM_ABI value_type load(memory_order __order = memory_order::seq_cst) const noexcept
       _LIBCPP_CHECK_LOAD_MEMORY_ORDER(__order) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
         __order == memory_order::relaxed || __order == memory_order::consume || __order == memory_order::acquire ||
             __order == memory_order::seq_cst,
         "atomic_ref: memory order argument to atomic load operation is invalid");
-    alignas(_Tp) byte __mem[sizeof(_Tp)];
-    auto* __ret = reinterpret_cast<_Tp*>(__mem);
+    alignas(value_type) byte __mem[sizeof(value_type)];
+    auto* __ret = reinterpret_cast<value_type*>(__mem);
     __atomic_load(__ptr_, __ret, std::__to_gcc_order(__order));
     return *__ret;
   }
 
-  _LIBCPP_HIDE_FROM_ABI operator _Tp() const noexcept { return load(); }
+  _LIBCPP_HIDE_FROM_ABI operator value_type() const noexcept { return load(); }
 
-  _LIBCPP_HIDE_FROM_ABI _Tp exchange(_Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
-    alignas(_Tp) byte __mem[sizeof(_Tp)];
-    auto* __ret = reinterpret_cast<_Tp*>(__mem);
+  _LIBCPP_HIDE_FROM_ABI value_type
+  exchange(value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    alignas(value_type) byte __mem[sizeof(value_type)];
+    auto* __ret = reinterpret_cast<value_type*>(__mem);
     __atomic_exchange(__ptr_, __clear_padding(__desired), __ret, std::__to_gcc_order(__order));
     return *__ret;
   }
 
-  _LIBCPP_HIDE_FROM_ABI bool
-  compare_exchange_weak(_Tp& __expected, _Tp __desired, memory_order __success, memory_order __failure) const noexcept
-      _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+  _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+      value_type& __expected, value_type __desired, memory_order __success, memory_order __failure) const noexcept
+    requires(!is_const_v<_Tp>)
+  _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
         __failure == memory_order::relaxed || __failure == memory_order::consume ||
             __failure == memory_order::acquire || __failure == memory_order::seq_cst,
@@ -172,9 +184,10 @@ struct __atomic_ref_base {
         std::__to_gcc_order(__success),
         std::__to_gcc_order(__failure));
   }
-  _LIBCPP_HIDE_FROM_ABI bool
-  compare_exchange_strong(_Tp& __expected, _Tp __desired, memory_order __success, memory_order __failure) const noexcept
-      _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
+  _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+      value_type& __expected, value_type __desired, memory_order __success, memory_order __failure) const noexcept
+    requires(!is_const_v<_Tp>)
+  _LIBCPP_CHECK_EXCHANGE_MEMORY_ORDER(__success, __failure) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
         __failure == memory_order::relaxed || __failure == memory_order::consume ||
             __failure == memory_order::acquire || __failure == memory_order::seq_cst,
@@ -188,8 +201,10 @@ struct __atomic_ref_base {
         std::__to_gcc_order(__failure));
   }
 
-  _LIBCPP_HIDE_FROM_ABI bool
-  compare_exchange_weak(_Tp& __expected, _Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI bool compare_exchange_weak(
+      value_type& __expected, value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
     return __compare_exchange(
         __ptr_,
         std::addressof(__expected),
@@ -198,8 +213,10 @@ struct __atomic_ref_base {
         std::__to_gcc_order(__order),
         std::__to_gcc_failure_order(__order));
   }
-  _LIBCPP_HIDE_FROM_ABI bool
-  compare_exchange_strong(_Tp& __expected, _Tp __desired, memory_order __order = memory_order::seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI bool compare_exchange_strong(
+      value_type& __expected, value_type __desired, memory_order __order = memory_order::seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
     return __compare_exchange(
         __ptr_,
         std::addressof(__expected),
@@ -209,7 +226,7 @@ struct __atomic_ref_base {
         std::__to_gcc_failure_order(__order));
   }
 
-  _LIBCPP_HIDE_FROM_ABI void wait(_Tp __old, memory_order __order = memory_order::seq_cst) const noexcept
+  _LIBCPP_HIDE_FROM_ABI void wait(value_type __old, memory_order __order = memory_order::seq_cst) const noexcept
       _LIBCPP_CHECK_WAIT_MEMORY_ORDER(__order) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
         __order == memory_order::relaxed || __order == memory_order::consume || __order == memory_order::acquire ||
@@ -217,8 +234,16 @@ struct __atomic_ref_base {
         "atomic_ref: memory order argument to atomic wait operation is invalid");
     std::__atomic_wait(*this, __old, __order);
   }
-  _LIBCPP_HIDE_FROM_ABI void notify_one() const noexcept { std::__atomic_notify_one(*this); }
-  _LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept { std::__atomic_notify_all(*this); }
+  _LIBCPP_HIDE_FROM_ABI void notify_one() const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    std::__atomic_notify_one(*this);
+  }
+  _LIBCPP_HIDE_FROM_ABI void notify_all() const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    std::__atomic_notify_all(*this);
+  }
 
 protected:
   using _Aligned_Tp [[__gnu__::__aligned__(required_alignment)]] = _Tp;
@@ -243,6 +268,8 @@ struct atomic_ref : public __atomic_ref_base<_Tp> {
 
   using __base = __atomic_ref_base<_Tp>;
 
+  static_assert(__base::is_always_lock_free || !is_volatile_v<_Tp>);
+
   _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
         reinterpret_cast<uintptr_t>(std::addressof(__obj)) % __base::required_alignment == 0,
@@ -251,17 +278,24 @@ struct atomic_ref : public __atomic_ref_base<_Tp> {
 
   _LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
 
-  _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
+  _LIBCPP_HIDE_FROM_ABI __base::value_type operator=(__base::value_type __desired) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return __base::operator=(__desired);
+  }
 
   atomic_ref& operator=(const atomic_ref&) = delete;
 };
 
 template <class _Tp>
-  requires(std::integral<_Tp> && !std::same_as<bool, _Tp>)
+  requires(std::integral<_Tp> && !std::same_as<bool, __remove_cv_t<_Tp>>)
 struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   using __base = __atomic_ref_base<_Tp>;
 
+  static_assert(__base::is_always_lock_free || !is_volatile_v<_Tp>);
+
   using difference_type = __base::value_type;
+  using value_type      = __base::value_type;
 
   _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
@@ -271,35 +305,90 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
 
   _LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
 
-  _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
+  _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return __base::operator=(__desired);
+  }
 
   atomic_ref& operator=(const atomic_ref&) = delete;
 
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_add(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<value_type>)
+  {
     return __atomic_fetch_add(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_sub(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<value_type>)
+  {
     return __atomic_fetch_sub(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_and(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_and(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<value_type>)
+  {
     return __atomic_fetch_and(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_or(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_or(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<value_type>)
+  {
     return __atomic_fetch_or(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_xor(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_xor(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<value_type>)
+  {
     return __atomic_fetch_xor(this->__ptr_, __arg, std::__to_gcc_order(__order));
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Tp operator++(int) const noexcept { return fetch_add(_Tp(1)); }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator--(int) const noexcept { return fetch_sub(_Tp(1)); }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator++() const noexcept { return fetch_add(_Tp(1)) + _Tp(1); }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator--() const noexcept { return fetch_sub(_Tp(1)) - _Tp(1); }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator&=(_Tp __arg) const noexcept { return fetch_and(__arg) & __arg; }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator|=(_Tp __arg) const noexcept { return fetch_or(__arg) | __arg; }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator^=(_Tp __arg) const noexcept { return fetch_xor(__arg) ^ __arg; }
+  _LIBCPP_HIDE_FROM_ABI value_type operator++(int) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_add(value_type(1));
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator--(int) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_sub(value_type(1));
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator++() const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_add(value_type(1)) + value_type(1);
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator--() const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_sub(value_type(1)) - value_type(1);
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator+=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_add(__arg) + __arg;
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator-=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_sub(__arg) - __arg;
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator&=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_and(__arg) & __arg;
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator|=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_or(__arg) | __arg;
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator^=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_xor(__arg) ^ __arg;
+  }
 };
 
 template <class _Tp>
@@ -307,7 +396,10 @@ template <class _Tp>
 struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
   using __base = __atomic_ref_base<_Tp>;
 
+  static_assert(__base::is_always_lock_free || !is_volatile_v<_Tp>);
+
   using difference_type = __base::value_type;
+  using value_type      = __base::value_type;
 
   _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __obj) : __base(__obj) {
     _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
@@ -317,56 +409,112 @@ struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
 
   _LIBCPP_HIDE_FROM_ABI atomic_ref(const atomic_ref&) noexcept = default;
 
-  _LIBCPP_HIDE_FROM_ABI _Tp operator=(_Tp __desired) const noexcept { return __base::operator=(__desired); }
+  _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return __base::operator=(__desired);
+  }
 
   atomic_ref& operator=(const atomic_ref&) = delete;
 
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_add(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new = __old + __arg;
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_add(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    value_type __old = this->load(memory_order_relaxed);
+    value_type __new = __old + __arg;
     while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
       __new = __old + __arg;
     }
     return __old;
   }
-  _LIBCPP_HIDE_FROM_ABI _Tp fetch_sub(_Tp __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    _Tp __old = this->load(memory_order_relaxed);
-    _Tp __new = __old - __arg;
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_sub(value_type __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    value_type __old = this->load(memory_order_relaxed);
+    value_type __new = __old - __arg;
     while (!this->compare_exchange_weak(__old, __new, __order, memory_order_relaxed)) {
       __new = __old - __arg;
     }
     return __old;
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Tp operator+=(_Tp __arg) const noexcept { return fetch_add(__arg) + __arg; }
-  _LIBCPP_HIDE_FROM_ABI _Tp operator-=(_Tp __arg) const noexcept { return fetch_sub(__arg) - __arg; }
+  _LIBCPP_HIDE_FROM_ABI value_type operator+=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_add(__arg) + __arg;
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator-=(value_type __arg) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_sub(__arg) - __arg;
+  }
 };
 
 template <class _Tp>
-struct atomic_ref<_Tp*> : public __atomic_ref_base<_Tp*> {
-  using __base = __atomic_ref_base<_Tp*>;
+  requires(std::is_pointer_v<_Tp>)
+struct atomic_ref<_Tp> : public __atomic_ref_base<_Tp> {
+  using __base = __atomic_ref_base<_Tp>;
 
   using difference_type = ptrdiff_t;
+  using value_type      = typename __base::value_type;
 
-  _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp*& __ptr) : __base(__ptr) {}
+  _LIBCPP_HIDE_FROM_ABI explicit atomic_ref(_Tp& __ptr) : __base(__ptr) {}
 
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator=(_Tp* __desired) const noexcept { return __base::operator=(__desired); }
+  _LIBCPP_HIDE_FROM_ABI value_type operator=(value_type __desired) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return __base::operator=(__desired);
+  }
 
   atomic_ref& operator=(const atomic_ref&) = delete;
 
-  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_add(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_add(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_add(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return __atomic_fetch_add(
+        this->__ptr_, __arg * sizeof(__remove_pointer_t<value_type>), std::__to_gcc_order(__order));
   }
-  _LIBCPP_HIDE_FROM_ABI _Tp* fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept {
-    return __atomic_fetch_sub(this->__ptr_, __arg * sizeof(_Tp), std::__to_gcc_order(__order));
+  _LIBCPP_HIDE_FROM_ABI value_type
+  fetch_sub(ptrdiff_t __arg, memory_order __order = memory_order_seq_cst) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return __atomic_fetch_sub(
+        this->__ptr_, __arg * sizeof(__remove_pointer_t<value_type>), std::__to_gcc_order(__order));
   }
 
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator++(int) const noexcept { return fetch_add(1); }
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator--(int) const noexcept { return fetch_sub(1); }
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator++() const noexcept { return fetch_add(1) + 1; }
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator--() const noexcept { return fetch_sub(1) - 1; }
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator+=(ptrdiff_t __arg) const noexcept { return fetch_add(__arg) + __arg; }
-  _LIBCPP_HIDE_FROM_ABI _Tp* operator-=(ptrdiff_t __arg) const noexcept { return fetch_sub(__arg) - __arg; }
+  _LIBCPP_HIDE_FROM_ABI value_type operator++(int) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_add(1);
+  }
+  _LIBCPP_HIDE_FROM_ABI value_type operator--(int) const noexcept
+    requires(!is_const_v<_Tp>)
+  {
+    return fetch_sub(1);
+  }
+  _LIBCPP_HIDE_FROM_ABI ...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/121414


More information about the libcxx-commits mailing list