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 } +}
