[Work-in-progress, posting to verify direction.]

Implement P0401R6, "Providing size feedback in the Allocator
interface".

libstdc++-v3/ChangeLog:
        PR libstdc++/118030
        * include/bits/alloc_traits.h (allocate_at_least, allocation_result,
        __at_least_result, __complete_type_allocator): Define.
        * include/bits/allocator.h (allocate_at_least): Define.
        * include/std/memory (__glibcxx_want_allocate_at_least): Define.
        * 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.
---
 libstdc++-v3/include/bits/alloc_traits.h      | 27 +++++++++++++++
 libstdc++-v3/include/bits/allocator.h         |  6 ++++
 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    | 34 +++++++++++++++++++
 .../allocator/allocate_at_least_neg.cc        | 22 ++++++++++++
 7 files changed, 108 insertions(+)
 create mode 100644 
libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
 create mode 100644 
libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc

diff --git a/libstdc++-v3/include/bits/alloc_traits.h 
b/libstdc++-v3/include/bits/alloc_traits.h
index c34143a3526..943bf03cc5a 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -548,6 +548,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     };
 #pragma GCC diagnostic pop
 
+#ifdef __glibcxx_lib_allocate_at_least  // C++23
+    template <typename _Pointer>
+      struct allocation_result
+      {
+       _Pointer ptr;
+       size_t count;
+      };
+
+    template <typename _Alloc>
+    using __at_least_result =
+      allocation_result<typename allocator_traits<_Alloc>::pointer>;
+
+    template <typename _Alloc>
+      concept __complete_type_allocator =
+       requires { sizeof(typename allocator_traits<_Alloc>::value_type); };
+
+    template <__complete_type_allocator _Alloc>
+      [[nodiscard]] constexpr __at_least_result<_Alloc>
+      allocate_at_least(_Alloc& __a, size_t __n)
+      {
+       if constexpr (requires { __a.allocate_at_least(__n); })
+         return __a.allocate_at_least(__n);
+       else
+         return { __a.allocate(__n), __n };
+      }
+#endif
+
 #if _GLIBCXX_HOSTED
   /**
    * @brief  Partial specialization for `std::allocator`
diff --git a/libstdc++-v3/include/bits/allocator.h 
b/libstdc++-v3/include/bits/allocator.h
index 9f9526bd5b0..9c246b77d93 100644
--- a/libstdc++-v3/include/bits/allocator.h
+++ b/libstdc++-v3/include/bits/allocator.h
@@ -203,6 +203,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        return __allocator_base<_Tp>::allocate(__n, 0);
       }
 
+#ifdef __glibcxx_allocate_at_least  // C++23
+      [[nodiscard]] constexpr allocation_result<_Tp*>
+      allocate_at_least(size_t __n)
+      { return { allocate(__n), __n }; }
+#endif
+
       [[__gnu__::__always_inline__]]
       constexpr void
       deallocate(_Tp* __p, size_t __n)
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index dbe95b8b79f..ecd3315d8a3 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 = 202106;
+    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 eee99847490..f0d9a0010ab 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 202106L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_allocate_at_least)
+#   define __cpp_lib_allocate_at_least 202106L
+#  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 c9c9224e599..cbe9f5ad200 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 00000000000..317e7518c35
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
@@ -0,0 +1,34 @@
+// { dg-do run { 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(std::size_t n)
+      { return { Base::allocate(2*n), 2*n }; }
+  };
+
+int main()
+{
+  std::allocator<char> native;
+  auto a1 = native.allocate_at_least(100);
+  static_assert(std::is_same_v<decltype(a1), std::allocation_result<char*>>);
+
+  auto a2 = std::allocate_at_least<char*>(native, 100);
+  static_assert(std::is_same_v<decltype(a2), std::allocation_result<char*>>);
+
+  A<char> custom;
+  auto a3 = std::allocate_at_least<char*>(custom, 100);
+  static_assert(std::is_same_v<decltype(a3), std::allocation_result<char*>>);
+  VERIFY(a1.count == 100);
+  VERIFY(a2.count == 100);
+  VERIFY(a3.count == 200);
+  custom.deallocate(a3.ptr, a3.count);
+  native.deallocate(a2.ptr, a2.count);
+  native.deallocate(a1.ptr, a1.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 00000000000..a377c758b47
--- /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(std::size_t n)
+      { return { Base::allocate(2*n), 2*n }; }
+  };
+
+struct incomplete;
+
+int main()
+{
+  A<incomplete> custom;
+  auto a3 = std::allocate_at_least<char*>(custom, 100); // { dg-error "" }
+}
-- 
2.52.0

Reply via email to