https://gcc.gnu.org/g:02926c0abb71e21123ccb3f6de1a8b48244aaa7d

commit r16-7986-g02926c0abb71e21123ccb3f6de1a8b48244aaa7d
Author: Nathan Myers <[email protected]>
Date:   Thu Mar 5 08:54:09 2026 -0500

    libstdc++: Add allocate_at_least (P0401) [PR118030]
    
    Implement proposals adopted for C++23:
    P0401R6, "Providing size feedback in the Allocator interface"
    P2652R2, "Disallow User Specialization of allocator_traits".
    
    This is the minimal conforming implementation, i.e. without the
    useful parts. Useful parts, to come in future patches, would
    include giving access to any extra storage reserved, and use of
    it in vector, string, and other contiguous containers.
    
    libstdc++-v3/ChangeLog:
            PR libstdc++/118030
            * include/bits/alloc_traits.h (allocate_at_least (2x)): Define.
            * include/bits/allocator.h (allocate_at_least): Define.
            * include/std/memory (__glibcxx_want_allocate_at_least): Define.
            * include/bits/memoryfwd.h (allocation_result): Define, #include
            <bits/version.h> first so that will work.
            * include/bits/version.def (allocate_at_least): Add.
            * include/bits/version.h: Regenerate.
            * testsuite/20_util/allocator/allocate_at_least.cc: New test.
            * testsuite/20_util/allocator/allocate_at_least_neg.cc: New test.

Diff:
---
 libstdc++-v3/include/bits/alloc_traits.h           | 40 +++++++++++++
 libstdc++-v3/include/bits/allocator.h              |  6 ++
 libstdc++-v3/include/bits/memoryfwd.h              | 13 +++++
 libstdc++-v3/include/bits/version.def              |  8 +++
 libstdc++-v3/include/bits/version.h                | 10 ++++
 libstdc++-v3/include/std/memory                    |  1 +
 .../20_util/allocator/allocate_at_least.cc         | 65 ++++++++++++++++++++++
 .../20_util/allocator/allocate_at_least_neg.cc     | 22 ++++++++
 8 files changed, 165 insertions(+)

diff --git a/libstdc++-v3/include/bits/alloc_traits.h 
b/libstdc++-v3/include/bits/alloc_traits.h
index c34143a35269..2be8ed561d41 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -404,6 +404,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          return __a.allocate(__n);
       }
 
+#ifdef __glibcxx_allocate_at_least  // C++23
+      /**
+       *  @brief  Allocate memory, generously.
+       *  @param  __a  An allocator.
+       *  @param  __n  The minimum number of objects to allocate space for.
+       *  @return Memory of suitable size and alignment for `n` or more
+       *  contiguous objects of type `value_type`.
+       *
+       *  Returns `a.allocate_at_least(n)` if that expression is
+       *  well-formed, else `{ a.allocate(n), n }`. When an allocator
+       *  is obliged to reserve more space than required for the cited
+       *  `n` objects, it may deliver the extra space to the caller.
+      */
+      [[nodiscard]] static constexpr auto
+      allocate_at_least(_Alloc& __a, size_type __n)
+       -> allocation_result<pointer, size_type>
+      {
+       if constexpr (requires { __a.allocate_at_least(__n); })
+         return __a.allocate_at_least(__n);
+       else
+         return { __a.allocate(__n), __n };
+      }
+#endif
+
       /**
        *  @brief  Deallocate memory.
        *  @param  __a  An allocator.
@@ -635,6 +659,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
       }
 
+#ifdef __glibcxx_allocate_at_least  // C++23
+      /**
+       *  @brief  Allocate memory, generously.
+       *  @param  __a  An allocator.
+       *  @param  __n  The minimum number of objects to allocate space for.
+       *  @return Memory of suitable size and alignment for `n` or more
+       *  contiguous objects of type `value_type`.
+       *
+       *  Returns `a.allocate_at_least(n)`.
+      */
+      [[nodiscard]] static constexpr auto
+      allocate_at_least(allocator_type __a, size_type __n)
+       -> allocation_result<pointer, size_type>
+      { return __a.allocate_at_least(__n); }
+#endif
+
       /**
        *  @brief  Deallocate memory.
        *  @param  __a  An allocator.
diff --git a/libstdc++-v3/include/bits/allocator.h 
b/libstdc++-v3/include/bits/allocator.h
index 9f9526bd5b01..a5e115265911 100644
--- a/libstdc++-v3/include/bits/allocator.h
+++ b/libstdc++-v3/include/bits/allocator.h
@@ -216,6 +216,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 #endif // C++20
 
+#ifdef __glibcxx_allocate_at_least  // C++23
+      [[nodiscard]] constexpr allocation_result<_Tp*, size_t>
+      allocate_at_least(size_t __n)
+      { return { this->allocate(__n), __n }; }
+#endif
+
       friend __attribute__((__always_inline__)) _GLIBCXX20_CONSTEXPR
       bool
       operator==(const allocator&, const allocator&) _GLIBCXX_NOTHROW
diff --git a/libstdc++-v3/include/bits/memoryfwd.h 
b/libstdc++-v3/include/bits/memoryfwd.h
index ca532ca39fad..9efe72b38ba0 100644
--- a/libstdc++-v3/include/bits/memoryfwd.h
+++ b/libstdc++-v3/include/bits/memoryfwd.h
@@ -48,6 +48,7 @@
 #endif
 
 #include <bits/c++config.h>
+#include <bits/version.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -78,6 +79,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct allocator_traits;
 #endif
 
+#ifdef __glibcxx_allocate_at_least  // C++23
+  // Result of, specifically, allocate_at_least(). `count` is the number
+  // of objects that may be indexed from `ptr`, not bytes.
+  template <typename _Pointer, typename _Size = size_t>
+    struct allocation_result
+    {
+      _Pointer ptr;
+      _Size count;
+    };
+#endif
+
+
   /// @} group memory
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index dbe95b8b79fd..dd77524b5a9e 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -88,6 +88,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = allocate_at_least;
+  values = {
+    v = 202302;
+    cxxmin = 23;
+  };
+};
+
 ftms = {
   name = is_null_pointer;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index eee998474907..bef61baeba8d 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -80,6 +80,16 @@
 #endif /* !defined(__cpp_lib_allocator_traits_is_always_equal) */
 #undef __glibcxx_want_allocator_traits_is_always_equal
 
+#if !defined(__cpp_lib_allocate_at_least)
+# if (__cplusplus >= 202100L)
+#  define __glibcxx_allocate_at_least 202302L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_allocate_at_least)
+#   define __cpp_lib_allocate_at_least 202302L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_allocate_at_least) */
+#undef __glibcxx_want_allocate_at_least
+
 #if !defined(__cpp_lib_is_null_pointer)
 # if (__cplusplus >= 201103L)
 #  define __glibcxx_is_null_pointer 201309L
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index c9c9224e5991..cbe9f5ad2007 100644
--- a/libstdc++-v3/include/std/memory
+++ b/libstdc++-v3/include/std/memory
@@ -125,6 +125,7 @@
 #define __glibcxx_want_to_address
 #define __glibcxx_want_transparent_operators
 #define __glibcxx_want_smart_ptr_owner_equality
+#define __glibcxx_want_allocate_at_least
 #include <bits/version.h>
 
 #if __cplusplus >= 201103L && __cplusplus <= 202002L && _GLIBCXX_HOSTED
diff --git a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc 
b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
new file mode 100644
index 000000000000..5399096d294b
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
@@ -0,0 +1,65 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+struct X { int i = 0; };
+
+template <typename T>
+  struct A : std::allocator<T>
+  {
+    using Base = std::allocator<T>;
+
+    std::allocation_result<T*, size_t>
+    allocate_at_least(std::size_t n)
+      { return { this->Base::allocate(2*n), 2*n }; }
+  };
+
+template <typename T>
+  struct M : std::allocator<T>
+  {
+    using Base = std::allocator<T>;
+    T* allocate_at_least(size_t n) = delete;
+
+    T* keep;
+    T* allocate(std::size_t n)
+      {
+       keep = this->Base::allocate(n);
+       return keep;
+      }
+  };
+
+int main()
+{
+  std::allocator<X> native;
+  auto a1 = native.allocate_at_least(100);
+  static_assert(std::is_same_v<decltype(a1), std::allocation_result<X*>>);
+  VERIFY(a1.count == 100);
+  native.deallocate(a1.ptr, a1.count);
+
+  using std_traits = std::allocator_traits<std::allocator<X>>;
+  auto a2 = std_traits::allocate_at_least(native, 100);
+  static_assert(std::is_same_v<decltype(a2), std::allocation_result<X*>>);
+  VERIFY(a2.count == 100);
+  std_traits::deallocate(native, a2.ptr, a2.count);
+
+  A<X> custom;
+  auto a3 = custom.allocate_at_least(100);
+  static_assert(std::is_same_v<decltype(a3), std::allocation_result<X*>>);
+  VERIFY(a3.count == 200);
+  custom.deallocate(a3.ptr, a3.count);
+
+  using custom_traits = std::allocator_traits<A<X>>;
+  auto a4 = custom_traits::allocate_at_least(custom, 100);
+  static_assert(std::is_same_v<decltype(a4), std::allocation_result<X*>>);
+  VERIFY(a4.count == 200);
+  custom_traits::deallocate(custom, a4.ptr, a4.count);
+
+  M<X> minimal;
+  using minimal_traits = std::allocator_traits<M<X>>;
+  auto a5 = minimal_traits::allocate_at_least(minimal, 100);
+  static_assert(std::is_same_v<decltype(a5), std::allocation_result<X*>>);
+  VERIFY(a5.count == 100);
+  VERIFY(a5.ptr == minimal.keep);
+  minimal_traits::deallocate(minimal, a5.ptr, a5.count);
+}
diff --git a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc 
b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc
new file mode 100644
index 000000000000..d0943d6974da
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc
@@ -0,0 +1,22 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+template <typename T>
+  struct A : std::allocator<T>
+  {
+    using Base = std::allocator<T>;
+    std::allocation_result<T*> allocate_at_least(size_t) = delete;
+    T* allocate(std::size_t n) { return this->Base::allocate(n); }
+  };
+
+struct incomplete;
+
+int main()
+{
+  A<incomplete> a;
+  using traits = std::allocator_traits<A<incomplete>>;
+  (void) traits::allocate_at_least(a, 1); // { dg-error "from here" }
+  // { dg-error "incomplete type" "" { target { *-*-* } } 0 }
+}

Reply via email to