Changes in v2:
 - Move 'struct allocation_result' so non-hosted users see it.
 - Use "`" markdown markup in new doxygen comments.
 - Remove redundant static_asserts re: incomplete types.

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.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      | 40 ++++++++++++
 libstdc++-v3/include/bits/allocator.h         |  9 +++
 libstdc++-v3/include/bits/memoryfwd.h         | 12 ++++
 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 +++++++++++++++++++
 .../allocator/allocate_at_least_neg.cc        | 24 +++++++
 8 files changed, 169 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..2be8ed561d4 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 9f9526bd5b0..aab6b48fe42 100644
--- a/libstdc++-v3/include/bits/allocator.h
+++ b/libstdc++-v3/include/bits/allocator.h
@@ -216,6 +216,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 #endif // C++20
 
+#ifdef __glibcxx_allocate_at_least  // C++23
+      // TODO: This should be altered to interact usefully with features
+      // of the libstdc++-provided ::operator new (as distinct from a
+      // hypothetical user-provided one the linker may substitute).
+      [[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 ca532ca39fa..9e6507978f5 100644
--- a/libstdc++-v3/include/bits/memoryfwd.h
+++ b/libstdc++-v3/include/bits/memoryfwd.h
@@ -78,6 +78,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 dbe95b8b79f..dd77524b5a9 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 eee99847490..bef61baeba8 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 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..5399096d294
--- /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 00000000000..7b50851b0f9
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least_neg.cc
@@ -0,0 +1,24 @@
+// { 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), 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 "object type must be complete" "" { target { *-*-* } } 0 }
+}
-- 
2.52.0

Reply via email to