[PATCHv3] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]

Jonathan Wakely jwakely@redhat.com
Tue May 12 19:18:26 GMT 2026


On Tue, 12 May 2026 at 20:12 +0100, Jonathan Wakely wrote:
>On Wed, 22 Apr 2026 at 00:16 -0400, Nathan Myers wrote:
>>[This patch retains the added config/abi/pre/gnu.ver string
>>symbol exports as if for gcc-16, although the planned ship
>>vehicle is gcc-17.]
>>
>>Changes in v3:
>>- Make allocator_traits<allocator<T>>::allocate_at_least
>> call allocator<T>::allocate_at_least directly, no checking
>> needed.
>>- Remove __allocate_to_alignment helper from alloc_traits.h,
>> folding it back into __new_allocator::allocate_at_least.
>> Replace use of std::rotl with op>>, saving one instruction
>> on x86.
>>- Change "if (is_constant_evaluated())" to "if consteval {"
>> where allowed. This required infelicitous changes to
>> indentation because, in "if consteval ... else {", the
>> bracket is (needlessly) required.
>>- Remove second, "hint" argument passed to allocate()
>> in fallback from allocator_traits<>::allocate_at_least,
>> as support for that has been deleted from the Standard.
>>- Remove attempt at pass-through from allocator_traits<
>> pmr::polymorphic_allocator>::allocate_at_least because
>> it does not provide that, and just call allocate(n).
>>- Retain minimalist vector<>::_M_allocate, used mainly
>> to get storage for vector<bool>.
>>- Rename new vector helper members _M_displace_storage
>> -> _M_replace_storage, and _M_allocate_and_migrate ->
>> _M_replace_with.
>>- Remove __glibcxx_want_allocate_at_least from <vector>
>> and <string> headers.
>>- Whitespace fixups.
>>
>>Changes in v2:
>>- Avoid PR108377 "exceeds maximum object size" warning
>> differently, contained to __allocate_to_alignment.
>>- Rename _S_allocate_to_alignment to __allocate_to_alignment
>> because it is not a member. Generalize it for arbitrary pointer
>> and size types.
>>- Declare allocator_traits<allocator<void>>::allocate_at_least
>> with " = delete;", matching others.
>>- In allocator_traits<>::allocate_at_least, de-structure and
>> reconstruct the allocation result to enable an implicit
>> pointer type conversion on the way, as used (e.g.) for
>> testsuite_allocator.h.
>>- Define allocate_at_least in the polymorphic specialization
>> of allocator_traits (bits/memory_resource.h), and in the
>> testsuite allocator, destructuring likewise.
>>- In string:
>>   - Make _M_create use _M_allocate_at_least. (This function
>>    is no longer used, but is retained for ABI stability.)
>>   - Define (in addition to _M_create_plus) a new allocation
>>    helper function _M_create_and_place to abstract common
>>    operations, and use it where suitable.
>>   - Add new member signatures to config/abi/pre/gnu.ver.
>>   - Relax shrink_to_fit()/reserve() to allow a little extra.
>>- In vector, define (on top of _S_allocate_at_least) helper
>> functions _M_allocate_and_copy, _M_displace_storage,
>> and _M_allocate_and_migrate to abstract common ops, and
>> use them. This incidentally corrects an omission of a
>> _GLIBCXX_ASAN_ANNOTATE_REINIT.
>>- Adjust tests to pass with or without allocate_at_least.
>> (__STDCPP_DEFAULT_NEW_ALIGNMENT__ is not defined everywhere.)
>>
>>Changes from RFC:
>>- Improve doxygen for new interfaces.
>>- Move code implementing alignment size-round-up logic from
>> allocator.h to alloc_traits.h for other allocators' reuse.
>>- Restore allocator.h definition of allocate_at_least, that
>> then explicitly delegates to its base class implementation.
>>- Retain existing string::_S_allocate, vector<>::_M_allocate
>> interfaces for ABI stability, using new names
>> _S_allocate_at_least, _M_allocate_at_least for new behavior.
>>- Define string _M_create_plus using S_allocate_at_least, and
>> use that throughout in place of _M_create, retaining _M_create
>> for ABI stability.
>>- Make memory_resource::allocate_at_least use argument
>> allocator's allocate_at_least if present.
>>- Fix std::allocator<>::allocate_at_least so it takes its
>> allocator object by reference, not by value.
>>- Per review, in constexpr context begin lifetime of all
>> potential string characters upon allocation.
>>- Revert "< 2011"-only vector<>::_M_initialize_dispatch changes.
>>- Fix string new-capacity off-by-one errors.
>>- Export new symbols _S_allocate_at_least and _M_create_plus from
>> bits/basic_string.h.
>>- Work around PR108377 spurious "exceeds maximum object size".
>>- Test.
>>- Patch too-strict capacity tests.
>>
>>Implements as much of `allocator<>::allocate_at_least` as possible
>>relying solely on known alignment behavior of standard operator
>>new.
>>
>>Uses `allocator_at_least` in `string` and `vector` to maximize
>>usage of actually allocated storage, as revealed by the
>>allocator in use. For user-supplied allocators this may make a
>>big difference.
>>
>>Nothing is changed in `include/ext/malloc_allocator.h` or
>>others.  They can be updated piecemeal. C++23 and C++26 do not
>>support `allocate_at_least` in `pmr` components, so there are
>>only minimal changes there.
>>
>>libstdc++-v3/ChangeLog:
>>	PR libstdc++/118030
>>	* include/bits/alloc_traits.h:
>>	(__allocate_to_alignment): Define.
>>	(allocate_at_least): In allocator_traits template member,
>>	destructure and reconstruct result from allocator object's version,
>>	to enable pointer type conversion.
>>	(allocate_at_least): Delegate in allocator_traits<allocator<_Tp>>
>>	specialization to allocator<_Tp>::allocate_at_least, unconditionally.
>>	(allocate_at_least): Declare "= delete;" in allocator<void>.
>>	* include/bits/allocator.h (allocate_at_least): Delegate to base
>>	allocate_at_least where defined, calling with explicit base-class
>>	qualification, picking up __new_allocator member.
>>	* include/bits/basic_string.h:
>>	(_Alloc_result): Define new type.
>>	(_S_allocate_at_least): Define.
>>	(_S_allocate): Delegate to _S_allocate_at_least.
>>	(_M_create_plus, _M_create_and_place): Declare.
>>	(assign): Use _S_allocate_at_least.
>>	* include/bits/basic_string.tcc:
>>	(_M_create_plus): Define.
>>	(_M_create_and_place): Define, abstracting common operations.
>>	(_M_replace, reserve): Use _S_allocate_at_least.
>>	(_M_construct, input iterators): Use _M_create_plus.
>>	(_M_construct, others (3x)): Use _M_create_and_place.
>>	(_M_create, _M_assign, reserve, _M_mutate): Use _M_create_plus.
>>	* include/bits/memory_resource.h (allocate_at_least): Define,
>>	document.
>>	* include/bits/new_allocator.h (allocate_at_least): Define, relying
>>	on __allocate_to_alignment from alloc_traits.h.
>>	(deallocate): Refine "if constexpr" logic.
>>	* include/bits/stl_vector.h:
>>	(_S_max_size): Move to _Vector_base.
>>	(_Alloc_result): Define type.
>>	(_M_allocate_at_least): Define, using allocate_at_least where supported.
>>	(_M_allocate): Delegate to _M_allocate_at_least.
>>	(max_size, _S_check_init_len): Use _S_max_size as moved.
>>	(_M_create_storage, append_range, _M_allocate_and_copy,
>>	_M_replace_storage): Define, abstracting common operations.
>>	(_M_replace_with): Define, likewise.
>>	(_M_range_initialize_n): Use _M_allocate_at_least.
>>	(_M_check_len): Improve logic.
>>	* include/bits/vector.tcc:
>>	(reserve, _M_fill_append, _M_range_insert): Use _M_allocate_at_least
>>	and _M_replace_storage.
>>	(operator=, _M_assign_aux): Use _M_replace_with.
>>	(_M_realloc_insert, _M_realloc_append, _M_default_append, insert_range):
>>	Use _M_allocate_at_least.
>>	(_M_fill_insert): Use _M_replace_storage, normalize whitespace.
>>	* testsuite/util/testsuite_allocator.h:
>>	(allocate_at_least (2x)): Define.
>>	(allocate): Use allocate_at_least.
>>	* testsuite/20_util/allocator/allocate_at_least.cc: Add tests.
>>	* testsuite/21_strings/basic_string/capacity/char/18654.cc:
>>	Loosen capacity check.
>>	* testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc:
>>	Same.
>>	* testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc: Same.
>>	* testsuite/21_strings/basic_string/capacity/wchar_t/2.cc: Same.
>>	* testsuite/21_strings/basic_string/capacity/wchar_t/shrink_to_fit.cc:
>>	Same.
>>	* testsuite/23_containers/vector/capacity/shrink_to_fit.cc: Same.
>>	* testsuite/23_containers/vector/capacity/shrink_to_fit2.cc: Same
>>	* testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc:
>>	Adapt to looser reserve behavior.
>>	* config/abi/pre/gnu.ver: Expose string::_S_allocate_at_least,
>>	_M_create_plus, and _M_create_and_place symbols.
>>---
>>libstdc++-v3/config/abi/pre/gnu.ver           |   4 +
>>libstdc++-v3/include/bits/alloc_traits.h      |  24 ++-
>>libstdc++-v3/include/bits/allocator.h         |  31 +++-
>>libstdc++-v3/include/bits/basic_string.h      |  43 ++++-
>>libstdc++-v3/include/bits/basic_string.tcc    | 105 +++++++-----
>>libstdc++-v3/include/bits/memory_resource.h   |  17 ++
>>libstdc++-v3/include/bits/new_allocator.h     |  60 ++++++-
>>libstdc++-v3/include/bits/stl_vector.h        | 138 ++++++++++++----
>>libstdc++-v3/include/bits/vector.tcc          | 155 +++++++-----------
>>.../20_util/allocator/allocate_at_least.cc    | 108 +++++++++++-
>>.../basic_string/capacity/char/18654.cc       |   7 +-
>>.../capacity/char/shrink_to_fit.cc            |   7 +-
>>.../basic_string/capacity/wchar_t/18654.cc    |   8 +-
>>.../basic_string/capacity/wchar_t/2.cc        |  13 +-
>>.../capacity/wchar_t/shrink_to_fit.cc         |   7 +-
>>.../vector/capacity/shrink_to_fit.cc          |   7 +-
>>.../vector/capacity/shrink_to_fit2.cc         |  14 +-
>>.../vector/modifiers/emplace/self_emplace.cc  |  36 ++--
>>.../testsuite/util/testsuite_allocator.h      |  36 ++++
>>19 files changed, 594 insertions(+), 226 deletions(-)
>>
>>diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
>>index bd4da641829..5dc9e1fdace 100644
>>--- a/libstdc++-v3/config/abi/pre/gnu.ver
>>+++ b/libstdc++-v3/config/abi/pre/gnu.ver
>>@@ -2621,6 +2621,10 @@ GLIBCXX_3.4.35 {
>>    _ZGIW3std;
>>    _ZGIW3stdW6compat;
>>
>>+    # basic_string::allocate_at_least
>>+    _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_S_allocate_*;
>>+    _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_M_create_*;
>
>I've pushed r17-471-ge79f0f818c0e42 which adds the GLIBCXX_3.4.37
>symbol version. Your new symbols should be exported in that version
>now, instead of GLIBCXX_3.4.36.
>
>>} GLIBCXX_3.4.34;
>>
>># Symbols in the support library (libsupc++) have their own tag.
>>diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
>>index e4e0f1608de..cdffeb1b31a 100644
>>--- a/libstdc++-v3/include/bits/alloc_traits.h
>>+++ b/libstdc++-v3/include/bits/alloc_traits.h
>>@@ -419,10 +419,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>      */
>>      [[nodiscard]] static constexpr auto
>>      allocate_at_least(_Alloc& __a, size_type __n)
>>-	-> allocation_result<pointer, size_type>
>>+      -> allocation_result<pointer, size_type>
>>      {
>>	if constexpr (requires { __a.allocate_at_least(__n); })
>>-	  return __a.allocate_at_least(__n);
>>+	  {
>>+	    // Destructure to enable implicit __p -> pointer conversion.
>>+	    auto [__p, __c] = __a.allocate_at_least(__n);
>>+	    return { __p, __c };
>
>I still didn't understand why this is needed, so I tested the patch
>without it.
>
>FAIL: 23_containers/vector/allocator/ext_ptr.cc  -std=gnu++23 (test for excess errors)
>Excess errors:
>/tmp/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/alloc_traits.h:425: error: could not convert '((std::allocator<T>*)(& __a))->std::allocator<T>::allocate_at_least(__n)' from 'allocation_result<T*,[...]>' to 'allocation_result<__gnu_cxx::_Pointer_adapter<__gnu_cxx::_Std_pointer_impl<T> >,[...]>'
>
>This is almost certainly a bug in the __gnu_test::CustomPointerAlloc,
>so should not be worked around in std::allocator_traits.

OK, I see the problem.

CustomPointerAlloc<T> provides its own allocate(size_t, void*) member
which wraps the allocate(size_t, void*) member from its
std::allocator<T> base class, and converts the T* to the derived
allocator's fancy pointer type.

The expression a.allocate_at_least(n) is valid because it finds the
member of the std::allocator<T> base class, so the requires-expression
evaluates to true. But the member it calls returns something that
isn't the right type, because the T* needs to be converted to the
fancy pointer.

So this is either a bug in __gnu_test::CustomPointerAlloc or a defect
in the standard (allocator_traits only cares whether the expression is
valid, but doesn't check if the return value is convertible to the
allocation_result type). But the answer is not to hack around it in
std::allocator_traits by performing an explicit conversion.

We should either add CustomPointerAlloc::allocate_at_least which does
the right conversion, or change the requires-expression to also check
the result type. I think for now we should just fix the test
allocator, and talk to LWG about it.




More information about the Libstdc++ mailing list