On Mon, 23 Feb 2026 at 15:26 +0800, Yuao Ma wrote:
Hi,

Previous implementations of fetch_min/max(r16-7097-g68a1218c189cce)
only supported integrals, not handling pointers. To complete the paper
P0493R5, we need to implement support for pointers as well. This patch
adds the missing functionality and test cases.

Oh well spotted.

Regression tested on x86_64-linux, ok for trunk?


Thanks,
Yuao

From 1fc8439ab78a6695fd67263ce31f452e1e12a356 Mon Sep 17 00:00:00 2001
From: Yuao Ma <[email protected]>
Date: Mon, 23 Feb 2026 15:15:56 +0800
Subject: [PATCH] libstdc++: complete P0493R5 with pointer support

Previous implementations of fetch_min/max only supported integers, not handling
pointers. To complete the paper, we need to implement support for pointers as
well. This patch adds the missing functionality and test cases.

libstdc++-v3/ChangeLog:

        * include/bits/atomic_base.h: Extend pointer support for atomic{, _ref}.

Please say what you actually changed. The GNU ChangeLog format is
documented here:
https://www.gnu.org/prep/standards/html_node/Style-of-Change-Logs.html

For this change it should be something like:

        * include/bits/atomic_base.h (__atomic_base<T*>::fetch_min)
        (__atomic_base<T*>::fetch_max): Define new functions.
        (__atomic_ref<P, false, false, true>::fetch_min): Likewise.
        (__atomic_ref<P, false, false, true>::fetch_max): Likewise.

        * include/std/atomic: Extend pointer support for nonmember funcs.

Again, this doesn't tell us what actually changed.

        * include/std/atomic (atomic_fetch_min_explicit): Change
        parameter from __atomic_base<I>* to atomic<I>*.
        (atomic_fetch_max_explicit, atomic_fetch_min)
        (atomic_fetch_max): Likewise.

        * testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc: New 
test.
        * testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc: New test.

These lines are too long, please add line breaks to keep them to
around 72 chars or less (when that's possible):

        * testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc:
        New test.
        * testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc: New
        test.

---
libstdc++-v3/include/bits/atomic_base.h       | 34 +++++++++
libstdc++-v3/include/std/atomic               | 38 +++++++---
.../atomic_integral/pointer_fetch_minmax.cc   | 53 ++++++++++++++
.../atomic_ref/pointer_fetch_minmax.cc        | 71 +++++++++++++++++++
4 files changed, 188 insertions(+), 8 deletions(-)
create mode 100644 
libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc
create mode 100644 
libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc

diff --git a/libstdc++-v3/include/bits/atomic_base.h 
b/libstdc++-v3/include/bits/atomic_base.h
index 79c1b31ccc4..6c5b83a0818 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -983,6 +983,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
      fetch_sub(ptrdiff_t __d,
                memory_order __m = memory_order_seq_cst) volatile noexcept
      { return __atomic_fetch_sub(&_M_p, _S_type_size(__d), int(__m)); }
+
+#if __glibcxx_atomic_min_max
+      _GLIBCXX_ALWAYS_INLINE __pointer_type
+      fetch_min(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) noexcept
+      { return __atomic_impl::__fetch_min(&_M_p, __p, __m); }
+
+      _GLIBCXX_ALWAYS_INLINE __pointer_type
+      fetch_min(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) volatile noexcept
+      { return __atomic_impl::__fetch_min(&_M_p, __p, __m); }
+
+      _GLIBCXX_ALWAYS_INLINE __pointer_type
+      fetch_max(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) noexcept
+      { return __atomic_impl::__fetch_max(&_M_p, __p, __m); }
+
+      _GLIBCXX_ALWAYS_INLINE __pointer_type
+      fetch_max(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) volatile noexcept
+      { return __atomic_impl::__fetch_max(&_M_p, __p, __m); }
+#endif
    };

  namespace __atomic_impl
@@ -1976,6 +1998,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                memory_order __m = memory_order_seq_cst) const noexcept
      { return __atomic_impl::fetch_sub(this->_M_ptr, _S_type_size(__d), __m); }

+#if __glibcxx_atomic_min_max
+      _GLIBCXX_ALWAYS_INLINE value_type
+      fetch_min(value_type __i,
+               memory_order __m = memory_order_seq_cst) const noexcept
+      { return __atomic_impl::__fetch_min(this->_M_ptr, __i, __m); }
+
+      _GLIBCXX_ALWAYS_INLINE value_type
+      fetch_max(value_type __i,
+               memory_order __m = memory_order_seq_cst) const noexcept
+      { return __atomic_impl::__fetch_max(this->_M_ptr, __i, __m); }
+#endif
+
      value_type
      operator++(int) const noexcept
      { return fetch_add(1); }
diff --git a/libstdc++-v3/include/std/atomic b/libstdc++-v3/include/std/atomic
index 306667e3c63..07c422d6e82 100644
--- a/libstdc++-v3/include/std/atomic
+++ b/libstdc++-v3/include/std/atomic
@@ -718,6 +718,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#endif
        return _M_b.fetch_sub(__d, __m);
      }
+
+#if __glibcxx_atomic_min_max
+      __pointer_type
+      fetch_min(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) noexcept
+      { return _M_b.fetch_min(__p, __m); }
+
+      __pointer_type
+      fetch_min(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) volatile noexcept
+      { return _M_b.fetch_min(__p, __m); }
+
+      __pointer_type
+      fetch_max(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) noexcept
+      { return _M_b.fetch_max(__p, __m); }
+
+      __pointer_type
+      fetch_max(__pointer_type __p,
+               memory_order __m = memory_order_seq_cst) volatile noexcept
+      { return _M_b.fetch_max(__p, __m); }
+#endif
    };


@@ -1576,28 +1598,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef __cpp_lib_atomic_min_max
  template<typename _ITp>
    inline _ITp
-    atomic_fetch_min_explicit(__atomic_base<_ITp>* __a,
+    atomic_fetch_min_explicit(atomic<_ITp>* __a,

I thought that maybe we need to constrain this to only be valid for
integer and pointer types, but actually the standard just says "If no
such member function exists, the program is ill-formed." in
[atomics.nonmembers].

Could you please change the _ITp parameter to _Tp in all these
atomic_fetch_{min,max}{,_explicit} functions though? Now that they're
not specific to integer types, _ITp is misleading.

                              __atomic_val_t<_ITp> __i,
                              memory_order __m) noexcept
    { return __a->fetch_min(__i, __m); }

  template<typename _ITp>
    inline _ITp
-    atomic_fetch_min_explicit(volatile __atomic_base<_ITp>* __a,
+    atomic_fetch_min_explicit(volatile atomic<_ITp>* __a,
                              __atomic_val_t<_ITp> __i,
                              memory_order __m) noexcept
    { return __a->fetch_min(__i, __m); }

  template<typename _ITp>
    inline _ITp
-    atomic_fetch_max_explicit(__atomic_base<_ITp>* __a,
+    atomic_fetch_max_explicit(atomic<_ITp>* __a,
                              __atomic_val_t<_ITp> __i,
                              memory_order __m) noexcept
    { return __a->fetch_max(__i, __m); }

  template<typename _ITp>
    inline _ITp
-    atomic_fetch_max_explicit(volatile __atomic_base<_ITp>* __a,
+    atomic_fetch_max_explicit(volatile atomic<_ITp>* __a,
                              __atomic_val_t<_ITp> __i,
                              memory_order __m) noexcept
    { return __a->fetch_max(__i, __m); }
@@ -1666,25 +1688,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#ifdef __cpp_lib_atomic_min_max
  template<typename _ITp>
    inline _ITp
-    atomic_fetch_min(__atomic_base<_ITp>* __a,
+    atomic_fetch_min(atomic<_ITp>* __a,
                     __atomic_val_t<_ITp> __i) noexcept
    { return atomic_fetch_min_explicit(__a, __i, memory_order_seq_cst); }

  template<typename _ITp>
    inline _ITp
-    atomic_fetch_min(volatile __atomic_base<_ITp>* __a,
+    atomic_fetch_min(volatile atomic<_ITp>* __a,
                     __atomic_val_t<_ITp> __i) noexcept
    { return atomic_fetch_min_explicit(__a, __i, memory_order_seq_cst); }

  template<typename _ITp>
    inline _ITp
-    atomic_fetch_max(__atomic_base<_ITp>* __a,
+    atomic_fetch_max(atomic<_ITp>* __a,
                     __atomic_val_t<_ITp> __i) noexcept
    { return atomic_fetch_max_explicit(__a, __i, memory_order_seq_cst); }

  template<typename _ITp>
    inline _ITp
-    atomic_fetch_max(volatile __atomic_base<_ITp>* __a,
+    atomic_fetch_max(volatile atomic<_ITp>* __a,
                     __atomic_val_t<_ITp> __i) noexcept
    { return atomic_fetch_max_explicit(__a, __i, memory_order_seq_cst); }
#endif
diff --git 
a/libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc 
b/libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc
new file mode 100644
index 00000000000..aca0c3157e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/pointer_fetch_minmax.cc

The 29_atomics/atomic_integral directory is for the integer
specializations, please put this new test file in 29_atomics/atomic
instead (we don't have a directory for atomic<T*> specializations,
maybe we should).

@@ -0,0 +1,53 @@
+// { dg-do run { target c++26 } }
+// { dg-require-atomic-cmpxchg-word "" }
+// { dg-add-options libatomic }
+
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01() {
+  long arr[10] = {};
+
+  const auto mo = std::memory_order_relaxed;
+  std::atomic<long *> a(arr);
+
+  auto v = atomic_fetch_max(&a, arr + 5);
+  VERIFY(v == arr);
+  VERIFY(a == arr + 5);
+  v = atomic_fetch_max_explicit(&a, arr + 2, mo);
+  VERIFY(v == arr + 5);
+  VERIFY(a == arr + 5);
+
+  v = atomic_fetch_min(&a, arr + 3);
+  VERIFY(v == arr + 5);
+  VERIFY(a == arr + 3);
+  v = atomic_fetch_min_explicit(&a, arr + 5, mo);
+  VERIFY(v == arr + 3);
+  VERIFY(a == arr + 3);
+}
+
+void test02() {
+  char arr[10] = {};
+
+  const auto mo = std::memory_order_relaxed;
+  std::atomic<char *> a(arr);
+
+  auto v = atomic_fetch_max(&a, arr + 5);
+  VERIFY(v == arr);
+  VERIFY(a == arr + 5);
+  v = atomic_fetch_max_explicit(&a, arr + 2, mo);
+  VERIFY(v == arr + 5);
+  VERIFY(a == arr + 5);
+
+  v = atomic_fetch_min(&a, arr + 3);
+  VERIFY(v == arr + 5);
+  VERIFY(a == arr + 3);
+  v = atomic_fetch_min_explicit(&a, arr + 5, mo);
+  VERIFY(v == arr + 3);
+  VERIFY(a == arr + 3);
+}
+
+int main() {
+  test01();
+  test02();
+}
diff --git 
a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc 
b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc
new file mode 100644
index 00000000000..25eef63ba99
--- /dev/null
+++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer_fetch_minmax.cc

This directory is fine though.

@@ -0,0 +1,71 @@
+// { dg-do run { target c++26 } }
+// { dg-require-atomic-cmpxchg-word "" }
+// { dg-add-options libatomic }
+
+#include <atomic>
+#include <testsuite_hooks.h>
+
+void test01() {
+  long arr[10] = {};
+  long *value;
+
+  {
+    const auto mo = std::memory_order_relaxed;
+    std::atomic_ref<long *> a(value);
+    bool ok = a.is_lock_free();
+    if constexpr (std::atomic_ref<long *>::is_always_lock_free)
+      VERIFY(ok);
+    a = arr;
+
+    auto v = a.fetch_max(arr + 5);
+    VERIFY(v == arr);
+    VERIFY(a == arr + 5);
+    v = a.fetch_max(arr + 2, mo);
+    VERIFY(v == arr + 5);
+    VERIFY(a == arr + 5);
+
+    v = a.fetch_min(arr + 3);
+    VERIFY(v == arr + 5);
+    VERIFY(a == arr + 3);
+    v = a.fetch_min(arr + 5, mo);
+    VERIFY(v == arr + 3);
+    VERIFY(a == arr + 3);
+  }
+
+  VERIFY(value == arr + 3);
+}
+
+void test02() {
+  char arr[10] = {};
+  char *value;
+
+  {
+    const auto mo = std::memory_order_relaxed;
+    std::atomic_ref<char *> a(value);
+    bool ok = a.is_lock_free();
+    if constexpr (std::atomic_ref<char *>::is_always_lock_free)
+      VERIFY(ok);
+    a = arr;
+
+    auto v = a.fetch_max(arr + 5);
+    VERIFY(v == arr);
+    VERIFY(a == arr + 5);
+    v = a.fetch_max(arr + 2, mo);
+    VERIFY(v == arr + 5);
+    VERIFY(a == arr + 5);
+
+    v = a.fetch_min(arr + 3);
+    VERIFY(v == arr + 5);
+    VERIFY(a == arr + 3);
+    v = a.fetch_min(arr + 5, mo);
+    VERIFY(v == arr + 3);
+    VERIFY(a == arr + 3);
+  }
+
+  VERIFY(value == arr + 3);
+}
+
+int main() {
+  test01();
+  test02();
+}
--
2.53.0


Reply via email to