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

Jonathan Wakely jwakely@redhat.com
Tue May 12 16:47:41 GMT 2026


On Tue, 21 Apr 2026 at 13:35 -0400, Nathan Myers wrote:
>Thank you.
>
>On 4/20/26 10:16 AM, Jonathan Wakely wrote:
>>On Sat, 18 Apr 2026 at 17:56 -0400, Nathan Myers wrote:
>>>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 (apparent?) omission
>>> of _GLIBCXX_ASAN_ANNOTATE_REINIT in operator=.
>>>- 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.
>>>
>>>Implement as much of allocator<>::allocate_at_least as possible
>>>relying solely on known alignment behavior of standard operator
>>>new. Provide apparatus for users' allocators to do the same.
>>>
>>>Use 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.
>>>
>>>Fixes omitted _GLIBCXX_ASAN_ANNOTATE_REINIT in vector<>::operator=.
>>>
>>>Nothing is changed in include/ext/malloc_allocator or others.
>>>They can be updated at leisure, piecemeal.
>>>
>>>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 member where defined.
>>>    (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/memoryfwd.h (__allocate_to_alignment): Declare.
>>>    * 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_displace_storage): Define, abstracting common operations.
>>>    (_M_allocate_and_migrate): 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_displace_storage.
>>>    (operator=, _M_assign_aux): Use _M_allocate_and_migrate.
>>>    (_M_realloc_insert, _M_realloc_append, _M_default_append, 
>>>insert_range):
>>>    Use _M_allocate_at_least.
>>>    (_M_fill_insert): Use _M_displace_storage, normalize whitespace.
>>>    * include/std/string: Define __glibcxx_want_allocate_at_least.
>>>    * include/std/vector: Same.
>>>    * 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           |   5 +
>>>libstdc++-v3/include/bits/alloc_traits.h      |  62 ++++++-
>>>libstdc++-v3/include/bits/allocator.h         |  28 +++-
>>>libstdc++-v3/include/bits/basic_string.h      |  38 ++++-
>>>libstdc++-v3/include/bits/basic_string.tcc    | 108 +++++++-----
>>>libstdc++-v3/include/bits/memory_resource.h   |  25 +++
>>>libstdc++-v3/include/bits/memoryfwd.h         |   6 +-
>>>libstdc++-v3/include/bits/new_allocator.h     |  41 ++++-
>>>libstdc++-v3/include/bits/stl_vector.h        | 132 +++++++++++----
>>>libstdc++-v3/include/bits/vector.tcc          | 155 +++++++-----------
>>>libstdc++-v3/include/std/string               |   1 +
>>>libstdc++-v3/include/std/vector               |   1 +
>>>.../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      |  35 ++++
>>>22 files changed, 614 insertions(+), 230 deletions(-)
>>>
>>>diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/ 
>>>config/abi/pre/gnu.ver
>>>index 624de951d41..bfb710cc9bc 100644
>>>--- a/libstdc++-v3/config/abi/pre/gnu.ver
>>>+++ b/libstdc++-v3/config/abi/pre/gnu.ver
>>>@@ -2606,6 +2606,11 @@ GLIBCXX_3.4.35 {
>>>_ZNSbIwSt11char_traitsIwESaIwEEC[12]EvQ26is_default_constructible_vIT1_E;
>>>_ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EEC[1-2]EvQ26is_default_constructible_vIT1_E;
>>>
>>>+    # std::basic_string:_S_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_*;
>>
>>This patch should wait for GCC 17 now, so the new symbols will need
>>to be in a new symbol version, GLIBCXX_3.4.36. We can deal with that
>>after the 16.1.0 release.
>
>ACK.
>
>
>>>#if defined (_WIN32) && !defined (__CYGWIN__)
>>>    _ZSt19__get_once_callablev;
>>>    _ZSt15__get_once_callv;
>>>diff --git a/libstdc++-v3/include/bits/alloc_traits.h 
>>>b/libstdc++-v3/ include/bits/alloc_traits.h
>>>index 2be8ed561d4..3ded07eba3c 100644
>>>--- a/libstdc++-v3/include/bits/alloc_traits.h
>>>+++ b/libstdc++-v3/include/bits/alloc_traits.h
>>>@@ -42,6 +42,9 @@
>>>#  include <bits/stl_iterator.h> // __make_move_if_noexcept_iterator
>>># endif
>>>#endif
>>>+#ifdef __glibcxx_allocate_at_least  // C++23
>>>+# include <bit>  // for rotl
>>>+#endif
>>>
>>>namespace std _GLIBCXX_VISIBILITY(default)
>>>{
>>>@@ -419,10 +422,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.
>>
>>Why is any conversion needed here?
>>
>>allocator_traits::pointer has to be the same type as the allocator
>>returns.
>
>This appeared to be necessary to get tests involving apparatus in
>testsuite/util/testsuite_allocator.* to build.
>
>
>>>+        auto [__p, __c] = __a.allocate_at_least(__n);
>>>+        return { __p, __c };
>>>+      }
>>>    else
>>>      return { __a.allocate(__n), __n };
>>>      }
>>>@@ -664,15 +671,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>       *  @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`.
>>>+       *  @return Memory of suitable size and alignment for `m >= n`
>>>+       *  contiguous objects of type `value_type`, and `m`.
>>>       *
>>>-       *  Returns `a.allocate_at_least(n)`.
>>>+       *  Returns `a.allocate_at_least(n)` if that is well-formed,
>>>+       *  or `{ a.allocate(n), n } otherwise.
>>>      */
>>>-      [[nodiscard]] static constexpr auto
>>>-      allocate_at_least(allocator_type __a, size_type __n)
>>>-    -> allocation_result<pointer, size_type>
>>>-      { return __a.allocate_at_least(__n); }
>>>+      [[nodiscard,__gnu__::__always_inline__]] static constexpr auto
>>
>>A newline after the attributes please
>
>ACK.
>
>
>>>+      allocate_at_least(allocator_type& __a, size_type __n)
>>>+      -> allocation_result<pointer, size_type>
>>>+      {
>>>+    if constexpr (requires
>>>+           { __a.allocate_at_least(__n); })
>>
>>Do we need this condition?
>>
>>std::allocator guarantees that the member is available. A
>>program-defined specialization of std::allocator needs to provide it
>>to be valid in C++26.
>
>ACK.
>
>
>>>+      return __a.allocate_at_least(__n);
>>>+    else
>>>+      return { __a.allocate(__n), __n };
>>>+      }
>>>#endif
>>>
>>>      /**
>>>@@ -822,6 +836,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      static void*
>>>      allocate(allocator_type&, size_type, const void* = nullptr) 
>>>= delete;
>>>
>>>+#ifdef __glibcxx_allocate_at_least
>>>+      static allocation_result<pointer, size_type>
>>>+      allocate_at_least(allocator_type&, size_type) = delete;
>>>+#endif
>>>+
>>>      /// deallocate is ill-formed for allocator<void>
>>>      static void
>>>      deallocate(allocator_type&, void*, size_type) = delete;
>>>@@ -874,6 +893,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>    };
>>>#endif
>>>
>>>+#ifdef __glibcxx_allocate_at_least
>>>+  template <typename _Pointer, typename _Size, typename _Alloc_fn>
>>>+    [[nodiscard]] constexpr auto
>>>+    __allocate_to_alignment(_Size __n, _Alloc_fn&& __af)
>>
>>This function is used in exactly one place, and is specific to
>>operator new because of the use of __STDCPP_DEFAULT_NEW_ALIGNMENT__ so
>>I think it would be better to just inline this code into
>>__new_allocator::allocate_at_least, or make this a private static
>>member function of __new_allocator.
>>
>>That way we know that n * objsize won't overflow (because we know that
>>it's already been checked by __new_allocator, whereas here it's
>>callable by any arbitrary code which might not have checked that).
>>
>>I also don't like passing in the allocation function, that seems ugly
>>and potentially an optimization barrier.
>
>I made a separate function because it seemed like it would be useful
>for users upgrading their own allocators.
>
>But I will fold it all back into __new_allocator::allocate_at_least,
>and let users copy and paste from that.
>
>
>>>+    -> allocation_result<_Pointer, _Size>
>>>+    {
>>>+      const _Size __align_mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>>>+      const _Size __objsize = sizeof(*declval<_Pointer>());
>>>+      if constexpr (__objsize > __align_mask)
>>>+    return { __af(__n * __objsize), __n };
>>>+      else
>>>+    {
>>>+      const _Size __need = __n * __objsize;
>>>+      _Size __ask = (__need + __align_mask) & ~__align_mask;
>>>+      // Avoid rounding up to and asking for 2^63 bytes (PR108377):
>>>+      __ask -= rotl(__ask, 1) & 1;
>>
>>Does this optimize as well as:
>>
>>     __ask -= bool(__ask & (_Size(1) << (__SIZE_WIDTH__ - 1)));
>>
>>?
>
>On x86, the rotl resolves to the same code, but does not depend
>on how big _Size is. Folded into __new_allocator, there is no
>uncertainty about that, so I have written
>
>  __ask -= __ask >> (__SIZE_WIDTH__ - 1);
>
>which is shorter and resolves to one instruction less. Compilers
>do not like generating a bit-test instruction for this, even though
>that helpfully sets the carry bit; probably something about leaving
>the instruction scheduler more freedom.
>
>
>>>+      using _U8 = const unsigned char;
>>>+      static_assert(__objsize <= ~_U8());
>>>+      // Use 8-bit division:
>>
>>Why?
>
>Div instruction cycle count strongly depends on operand size,
>particularly on smaller targets.
>
>
>>>+      _U8 __spare = __ask - __need, __size = __objsize;
>>>+      return { __af(__ask), __n + __spare / __size };
>>>+    }
>>>+    }
>>>+#endif
>>>+
>>>  /// @cond undocumented
>>>#pragma GCC diagnostic push
>>>#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>>>diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/ 
>>>include/bits/allocator.h
>>>index 9c22c805ebe..790edfaba41 100644
>>>--- a/libstdc++-v3/include/bits/allocator.h
>>>+++ b/libstdc++-v3/include/bits/allocator.h
>>>@@ -193,9 +193,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      constexpr _Tp*
>>>      allocate(size_t __n)
>>>      {
>>>-#if __cpp_concepts
>>>+# if __cpp_concepts
>>>    if constexpr (requires { sizeof(_Tp); })
>>>-#endif
>>>+# endif
>>>    if (std::__is_constant_evaluated())
>>>      {
>>>        if (__builtin_mul_overflow(__n, sizeof(_Tp), &__n))
>>>@@ -205,7 +205,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>
>>>    return __allocator_base<_Tp>::allocate(__n, 0);
>>>      }
>>>+#endif
>>>+
>>>+#ifdef __glibcxx_allocate_at_least  // C++23
>>>+      [[nodiscard,__gnu__::__always_inline__]]
>>>+      constexpr auto
>>>+      allocate_at_least(size_t __n)
>>>+      -> allocation_result<_Tp*, size_t>
>>>+      {
>>>+    if (std::is_constant_evaluated())
>>
>>It's preferable to use if consteval in code that doesn't need to
>>compile as <= C++20. It is less work for the compiler.
>
>ACK.
>It is unfortunate how its demand for a block after its `else`
>affects formatting.
>
>
>>>+      return { allocate(__n), __n };
>>>+    else if constexpr (requires
>>>+           { __allocator_base<_Tp>::allocate_at_least(__n); })
>>>+      return __allocator_base<_Tp>::allocate_at_least(__n);
>>>+    else
>>>+      return { __allocator_base<_Tp>::allocate(__n, 0), __n };
>>
>>You don't need to pass a hint argument to allocate
>>(we might remove the second parameter some day, since the two-argument
>>form of std::allocator::allocate was deprecated and removed).
>
>ACK.
>
>
>>>+      }
>>>+#endif
>>>
>>>+#if __cpp_constexpr_dynamic_alloc // >= C++20
>>>      [[__gnu__::__always_inline__]]
>>>      constexpr void
>>>      deallocate(_Tp* __p, size_t __n)
>>>@@ -219,12 +237,6 @@ _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/basic_string.h 
>>>b/libstdc++-v3/ include/bits/basic_string.h
>>>index af4e5d9486f..98bffa50f32 100644
>>>--- a/libstdc++-v3/include/bits/basic_string.h
>>>+++ b/libstdc++-v3/include/bits/basic_string.h
>>>@@ -135,20 +135,35 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>>>#endif
>>>
>>>    private:
>>>+      // For ABI reasons this must remain, though unused in active code.
>>>      static _GLIBCXX20_CONSTEXPR pointer
>>>      _S_allocate(_Char_alloc_type& __a, size_type __n)
>>>+      { return _S_allocate_at_least(__a, __n).__ptr; }
>>
>>This means that all callers of this function in objects compiled with
>>older versions of GCC will now execute the extra instructions in
>>allocate_at_least to round up the size, and then ignore it. That goes
>>against "you don't pay for what you don't use".
>>
>>It seems better to keep the old behaviour of this function.
>
>If restored, it would anyway only be used in a consteval context
>in _M_replace.

The point is that existing callers in object files that were already
compiled have references to the _S_allocate symbol. Those existing
callers will now call the new definition of this symbol, which does
extra work (to round up the allocation size) and then throws that
away.

If the old _S_allocate function has the original behaviour, those
existing callers can keep using it and get exactly the behaviour they
always had until now.


>
>
>>>+
>>>+      struct _Alloc_result { pointer __ptr; size_type __count; };
>>>+
>>>+      static _GLIBCXX20_CONSTEXPR _Alloc_result
>>>+      _S_allocate_at_least(_Char_alloc_type& __a, size_type __n)
>>>      {
>>>-    pointer __p = _Alloc_traits::allocate(__a, __n);
>>>+    _Alloc_result __r;
>>>+#ifdef __glibcxx_allocate_at_least  // C++23
>>>+    auto [__ptr, __count] = _Alloc_traits::allocate_at_least(__a, __n);
>>>+    __r.__ptr = __ptr;
>>>+    __r.__count = __count;
>>>+#else
>>>+    __r.__ptr = _Alloc_traits::allocate(__a, __n);
>>>+    __r.__count = __n;
>>>+#endif
>>>#if __glibcxx_constexpr_string >= 201907L
>>>    // std::char_traits begins the lifetime of characters,
>>>    // but custom traits might not, so do it here.
>>>    if constexpr (!is_same_v<_Traits, char_traits<_CharT>>)
>>>      if (std::__is_constant_evaluated())
>>>        // Begin the lifetime of characters in allocated storage.
>>>-        for (size_type __i = 0; __i < __n; ++__i)
>>>-          std::construct_at(__builtin_addressof(__p[__i]));
>>>+        for (size_type __i = 0; __i < __r.__count; ++__i)
>>>+          std::construct_at(__builtin_addressof(__r.__ptr[__i]));
>>>#endif
>>>-    return __p;
>>>+    return __r;
>>>      }
>>>
>>>#ifdef __glibcxx_string_view // >= C++17
>>>@@ -287,6 +302,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>>>
>>>      // Create & Destroy
>>>      _GLIBCXX20_CONSTEXPR
>>>+      _Alloc_result
>>>+      _M_create_plus(size_type __new_capacity, size_type 
>>>__old_capacity);
>>>+
>>>+      _GLIBCXX20_CONSTEXPR
>>>+      void
>>>+      _M_create_and_place(size_type __new_capacity, size_type 
>>>__old_capacity);
>>>+
>>>+      // This must remain for ABI stability though unused.
>>>+      _GLIBCXX20_CONSTEXPR
>>>      pointer
>>>      _M_create(size_type&, size_type);
>>>
>>>@@ -1782,10 +1806,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>>>            const auto __len = __str.size();
>>>            auto __alloc = __str._M_get_allocator();
>>>            // If this allocation throws there are no effects:
>>>-            auto __ptr = _S_allocate(__alloc, __len + 1);
>>>+            auto __r = _S_allocate_at_least(__alloc, __len + 1);
>>>            _M_destroy(_M_allocated_capacity);
>>>-            _M_data(__ptr);
>>>-            _M_capacity(__len);
>>>+            _M_data(__r.__ptr);
>>>+            _M_capacity(__r.__count - 1);
>>>            _M_set_length(__len);
>>>          }
>>>          }
>>>diff --git a/libstdc++-v3/include/bits/basic_string.tcc 
>>>b/libstdc++- v3/include/bits/basic_string.tcc
>>>index b00dd550237..aaf8df26b1a 100644
>>>--- a/libstdc++-v3/include/bits/basic_string.tcc
>>>+++ b/libstdc++-v3/include/bits/basic_string.tcc
>>>@@ -139,14 +139,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>
>>>  template<typename _CharT, typename _Traits, typename _Alloc>
>>>    _GLIBCXX20_CONSTEXPR
>>>-    typename basic_string<_CharT, _Traits, _Alloc>::pointer
>>>+    typename basic_string<_CharT, _Traits, _Alloc>::_Alloc_result
>>>    basic_string<_CharT, _Traits, _Alloc>::
>>>-    _M_create(size_type& __capacity, size_type __old_capacity)
>>>+    _M_create_plus(size_type __capacity, size_type __old_capacity)
>>>    {
>>>      // _GLIBCXX_RESOLVE_LIB_DEFECTS
>>>      // 83.  String::npos vs. string::max_size()
>>>      if (__capacity > max_size())
>>>-    std::__throw_length_error(__N("basic_string::_M_create"));
>>>+    std::__throw_length_error(__N("basic_string::_M_create_plus"));
>>>
>>>      // The below implements an exponential growth policy, necessary to
>>>      // meet amortized linear time requirements of the library: see
>>>@@ -161,7 +161,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>
>>>      // NB: Need an array of char_type[__capacity], plus a terminating
>>>      // null char_type() element.
>>>-      return _S_allocate(_M_get_allocator(), __capacity + 1);
>>>+      return _S_allocate_at_least(_M_get_allocator(), __capacity + 1);
>>>+    }
>>>+
>>>+
>>>+  template<typename _CharT, typename _Traits, typename _Alloc>
>>>+    _GLIBCXX20_CONSTEXPR
>>>+    void
>>>+    basic_string<_CharT, _Traits, _Alloc>::
>>>+    _M_create_and_place(size_type __capacity, size_type __old_capacity)
>>>+    {
>>>+      _Alloc_result __r = _M_create_plus(__capacity, __old_capacity);
>>>+      _M_data(__r.__ptr);
>>>+      _M_capacity(__r.__count - 1);  // Leave room for NUL.
>>>+    }
>>>+
>>>+  // This must remain for ABI stability, though unused in current code.
>>>+  template<typename _CharT, typename _Traits, typename _Alloc>
>>>+    _GLIBCXX20_CONSTEXPR
>>>+    typename basic_string<_CharT, _Traits, _Alloc>::pointer
>>>+    basic_string<_CharT, _Traits, _Alloc>::
>>>+    _M_create(size_type& __capacity, size_type __old_capacity)
>>>+    {
>>>+      _Alloc_result __r = _M_create_plus(__capacity, __old_capacity);
>>
>>Again, this makes existing callers of _M_create perform more work,
>>although in this case they will actually use the extra capacity, so
>>it's not wasted work.
>
>Yes, _M_create performs much the same work as _M_create_plus, just
>with a really awful usage interface.
>
>
>>>+      __capacity = __r.__count - 1;  // Leave room for NUL.
>>>+      return __r.__ptr;
>>>    }
>>>
>>>  // NB: This is the special case for Input Iterators, used in
>>>@@ -203,11 +227,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>        if (__len == __capacity)
>>>          {
>>>        // Allocate more space.
>>>-        __capacity = __len + 1;
>>>-        pointer __another = _M_create(__capacity, __len);
>>>-        this->_S_copy(__another, _M_data(), __len);
>>>+        _Alloc_result __another = _M_create_plus(__len + 1, __len);
>>>+        __capacity = __another.__count - 1; // Leave room for NUL.
>>>+        this->_S_copy(__another.__ptr, _M_data(), __len);
>>>        _M_dispose();
>>>-        _M_data(__another);
>>>+        _M_data(__another.__ptr);
>>>        _M_capacity(__capacity);
>>>          }
>>>        traits_type::assign(_M_data()[__len++],
>>>@@ -231,10 +255,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>    size_type __dnew = static_cast<size_type>(std::distance(__beg, 
>>>__end));
>>>
>>>    if (__dnew > size_type(_S_local_capacity))
>>>-      {
>>>-        _M_data(_M_create(__dnew, size_type(0)));
>>>-        _M_capacity(__dnew);
>>>-      }
>>>+      _M_create_and_place(__dnew, size_type(0));
>>>    else
>>>      _M_init_local_buf();
>>>
>>>@@ -264,10 +285,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>    _M_construct(size_type __n, _CharT __c)
>>>    {
>>>      if (__n > size_type(_S_local_capacity))
>>>-    {
>>>-      _M_data(_M_create(__n, size_type(0)));
>>>-      _M_capacity(__n);
>>>-    }
>>>+    _M_create_and_place(__n, size_type(0));
>>>      else
>>>    _M_init_local_buf();
>>>
>>>@@ -281,16 +299,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>  // than difference between iterators.
>>>  template<typename _CharT, typename _Traits, typename _Alloc>
>>>    template<bool _Terminated>
>>>-    _GLIBCXX20_CONSTEXPR
>>>+    _GLIBCXX20_CONSTEXPR
>>>    void
>>>    basic_string<_CharT, _Traits, _Alloc>::
>>>    _M_construct(const _CharT* __str, size_type __n)
>>>    {
>>>      if (__n > size_type(_S_local_capacity))
>>>-    {
>>>-      _M_data(_M_create(__n, size_type(0)));
>>>-      _M_capacity(__n);
>>>-    }
>>>+    _M_create_and_place(__n, size_type(0));
>>>      else
>>>    _M_init_local_buf();
>>>
>>>@@ -347,11 +362,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>
>>>      if (__rsize > __capacity)
>>>        {
>>>-          size_type __new_capacity = __rsize;
>>>-          pointer __tmp = _M_create(__new_capacity, __capacity);
>>>+          // if _M_create_plus throws, there is no effect.
>>>+          _Alloc_result __tmp = _M_create_plus(__rsize, __capacity);
>>>          _M_dispose();
>>>-          _M_data(__tmp);
>>>-          _M_capacity(__new_capacity);
>>>+          _M_data(__tmp.__ptr);
>>>+          _M_capacity(__tmp.__count - 1);
>>>        }
>>>
>>>      if (__rsize)
>>>@@ -375,11 +390,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      if (__res <= __capacity)
>>>    return;
>>>
>>>-      pointer __tmp = _M_create(__res, __capacity);
>>>-      this->_S_copy(__tmp, _M_data(), length() + 1);
>>>+      _Alloc_result __r = _M_create_plus(__res, __capacity);
>>>+      this->_S_copy(__r.__ptr, _M_data(), length() + 1);
>>>      _M_dispose();
>>>-      _M_data(__tmp);
>>>-      _M_capacity(__res);
>>>+      _M_data(__r.__ptr);
>>>+      _M_capacity(__r.__count - 1);  // Leave room for NUL.
>>>    }
>>>
>>>  template<typename _CharT, typename _Traits, typename _Alloc>
>>>@@ -392,19 +407,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      const size_type __how_much = length() - __pos - __len1;
>>>
>>>      size_type __new_capacity = length() + __len2 - __len1;
>>>-      pointer __r = _M_create(__new_capacity, capacity());
>>>+      _Alloc_result __r = _M_create_plus(__new_capacity, capacity());
>>>
>>>      if (__pos)
>>>-    this->_S_copy(__r, _M_data(), __pos);
>>>+    this->_S_copy(__r.__ptr, _M_data(), __pos);
>>>      if (__s && __len2)
>>>-    this->_S_copy(__r + __pos, __s, __len2);
>>>+    this->_S_copy(__r.__ptr + __pos, __s, __len2);
>>>      if (__how_much)
>>>-    this->_S_copy(__r + __pos + __len2,
>>>+    this->_S_copy(__r.__ptr + __pos + __len2,
>>>              _M_data() + __pos + __len1, __how_much);
>>>
>>>      _M_dispose();
>>>-      _M_data(__r);
>>>-      _M_capacity(__new_capacity);
>>>+      _M_data(__r.__ptr);
>>>+      _M_capacity(__r.__count - 1);  // Leave room for NUL.
>>>    }
>>>
>>>  template<typename _CharT, typename _Traits, typename _Alloc>
>>>@@ -430,6 +445,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      if (_M_is_local())
>>>    return;
>>>
>>>+# ifdef __glibcxx_allocate_at_least  // C++23
>>>+      const size_type __limit = (__STDCPP_DEFAULT_NEW_ALIGNMENT__ 
>>>- 1) / sizeof(_CharT);
>>>+# else
>>>+      const size_type __limit = 0;
>>>+# endif
>>>      const size_type __length = length();
>>>      const size_type __capacity = _M_allocated_capacity;
>>>
>>>@@ -441,14 +461,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      _M_data(_M_local_data());
>>>    }
>>>#if __cpp_exceptions
>>>-      else if (__length < __capacity)
>>>+      else if (__capacity - __length > __limit )
>>>    try
>>>      {
>>>-        pointer __tmp = _S_allocate(_M_get_allocator(), __length + 1);
>>>-        this->_S_copy(__tmp, _M_data(), __length + 1);
>>>+        _Alloc_result __r = _S_allocate_at_least(
>>>+          _M_get_allocator(), __length + 1);
>>>+        this->_S_copy(__r.__ptr, _M_data(), __length + 1);
>>>        _M_dispose();
>>>-        _M_data(__tmp);
>>>-        _M_capacity(__length);
>>>+        _M_data(__r.__ptr);
>>>+        _M_capacity(__r.__count - 1);  // reserve room for NUL.
>>>      }
>>>    catch (const __cxxabiv1::__forced_unwind&)
>>>      { throw; }
>>>@@ -588,7 +609,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>#if __cpp_lib_is_constant_evaluated
>>>      if (std::is_constant_evaluated())
>>>        {
>>>-          auto __newp = _S_allocate(_M_get_allocator(), __new_size);
>>>+          auto __newp =
>>>+        _S_allocate_at_least(_M_get_allocator(), __new_size).__ptr;
>>
>>If this branch isn't going to use the excess capacity, why bother
>>using _S_allocate_at_least? _S_allocate seems simpler here.
>
>This is a consteval context, where _S_allocate_at_least just calls
>allocate(n) anyway. This would be the only use of _S_allocate,
>otherwise only an ABI-stability stub.

Hmm, I guess so.

>
>>>          _S_copy(__newp, this->_M_data(), __pos);
>>>          _S_copy(__newp + __pos, __s, __len2);
>>>          _S_copy(__newp + __pos + __len2, __p + __len1, __how_much);
>>>@@ -677,7 +699,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>#endif // C++11
>>>
>>>#endif  // _GLIBCXX_USE_CXX11_ABI
>>>-
>>>+
>>>#if __glibcxx_constexpr_string >= 201907L
>>># define _GLIBCXX_STRING_CONSTEXPR constexpr
>>>#else
>>>@@ -916,7 +938,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>          // Avoid reallocation for common case.
>>>          __str.erase();
>>>          _CharT __buf[128];
>>>-          __size_type __len = 0;
>>>+          __size_type __len = 0;
>>>          const streamsize __w = __in.width();
>>>          const __size_type __n = __w > 0 ? static_cast<__size_type>(__w)
>>>                                      : __str.max_size();
>>>diff --git a/libstdc++-v3/include/bits/memory_resource.h 
>>>b/libstdc++- v3/include/bits/memory_resource.h
>>>index e5c6697b07e..0a0f799437b 100644
>>>--- a/libstdc++-v3/include/bits/memory_resource.h
>>>+++ b/libstdc++-v3/include/bits/memory_resource.h
>>>@@ -48,6 +48,7 @@
>>># include <bits/utility.h>        // index_sequence
>>># include <tuple>            // tuple, forward_as_tuple
>>>#endif
>>>+#include <bits/memoryfwd.h>
>>>
>>>namespace std _GLIBCXX_VISIBILITY(default)
>>>{
>>>@@ -468,6 +469,30 @@ namespace pmr
>>>      allocate(allocator_type& __a, size_type __n, const_void_pointer)
>>>      { return __a.allocate(__n); }
>>>
>>>+#ifdef __glibcxx_allocate_at_least
>>>+      /**
>>>+       *  @brief  Allocate memory, generously.
>>>+       *  @param  __a  An allocator.
>>>+       *  @param  __n  The number of objects to allocate space for.
>>>+       *  @return Memory of suitable size and alignment for `m >= n`
>>>+       *          objects of type `value_type`, and `m`.
>>>+       *
>>>+       *  Returns `a.allocate_at_least(n)` if it exists, otherwise
>>>+       *  `a.allocate(n)`.
>>>+      */
>>>+      [[nodiscard]] static auto
>>>+      allocate_at_least(allocator_type& __a, size_type __n)
>>>+      -> std::allocation_result<pointer, size_type>
>>>+      {
>>>+    if constexpr (requires { __a.allocate_at_least(__n); })
>>
>>pmr::polymorphic_allocator doesn't have allocate_at_least so this
>>condition is never true.
>>
>>We do want allocate_at_least in this allocator_traits partial
>>specialization but it should just return { __a.allocate(__n), __n}
>>unconditionally.
>
>ACK.
>I guess when pmr::polymorphic_allocator gets allocate_at_least,
>we can revisit.

I don't think it will ever get it. It would need a new virtual
function, which would be an ABI break.

>>>+      {
>>>+        auto [__p, __c] =    __a.allocate_at_least(__n);
>>>+        return { __p, __c };  // Enable conversion en route.
>>>+      }
>>>+    else return { __a.allocate(__n), __n };
>>>+      }
>>>+#endif
>>>+
>>>      /**
>>>       *  @brief  Deallocate memory.
>>>       *  @param  __a  An allocator.
>>>diff --git a/libstdc++-v3/include/bits/memoryfwd.h b/libstdc++-v3/ 
>>>include/bits/memoryfwd.h
>>>index 9efe72b38ba..6742f75499e 100644
>>>--- a/libstdc++-v3/include/bits/memoryfwd.h
>>>+++ b/libstdc++-v3/include/bits/memoryfwd.h
>>>@@ -88,8 +88,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      _Pointer ptr;
>>>      _Size count;
>>>    };
>>>-#endif
>>>
>>>+  template <typename _Pointer, typename _Size, typename _Alloc_fn>
>>>+    [[nodiscard]] constexpr auto
>>>+    __allocate_to_alignment(_Size __n, _Alloc_fn&& __af)
>>>+    -> allocation_result<_Pointer, _Size>;
>>>+#endif
>>>
>>>  /// @} group memory
>>>
>>>diff --git a/libstdc++-v3/include/bits/new_allocator.h 
>>>b/libstdc++-v3/ include/bits/new_allocator.h
>>>index fbe03e392aa..9a3f245f2aa 100644
>>>--- a/libstdc++-v3/include/bits/new_allocator.h
>>>+++ b/libstdc++-v3/include/bits/new_allocator.h
>>>@@ -37,6 +37,7 @@
>>>#if __cplusplus >= 201103L
>>>#include <type_traits>
>>>#endif
>>>+#include <bits/memoryfwd.h>
>>>
>>>namespace std _GLIBCXX_VISIBILITY(default)
>>>{
>>>@@ -150,6 +151,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>          std::__throw_bad_array_new_length();
>>>        std::__throw_bad_alloc();
>>>      }
>>>+
>>>#if __cpp_aligned_new && __cplusplus >= 201103L
>>>    else if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>>>      {
>>>@@ -162,6 +164,41 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
>>>      }
>>>
>>>+#ifdef __glibcxx_allocate_at_least  // C++23
>>>+      [[nodiscard]] constexpr auto
>>>+      allocate_at_least(size_t __n)
>>>+      -> std::allocation_result<_Tp*, size_t>
>>>+      {
>>>+    static_assert(requires { sizeof(_Tp); },
>>>+      "cannot allocate incomplete types");
>>>+
>>>+    if constexpr (!requires { sizeof(_Tp); })
>>>+      return { nullptr, 0 }; // static_assert already failed
>>>+    else if (__builtin_expect(__n > this->_M_max_size(), false))
>>>+      {
>>>+        // _GLIBCXX_RESOLVE_LIB_DEFECTS
>>>+        // 3190. allocator::allocate sometimes returns too little 
>>>storage
>>>+        if (__n > (std::size_t(-1) / sizeof(_Tp)))
>>>+          std::__throw_bad_array_new_length();
>>>+        std::__throw_bad_alloc();
>>>+      }
>>>+    else if (std::__is_constant_evaluated())
>>
>>This should be 'if consteval'
>
>ACK.
>
>
>>>+      {
>>>+        auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp));
>>>+        return { static_cast<_Tp*>(__p), __n };
>>>+      }
>>>+    else if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>>>+      {
>>>+        const std::align_val_t __al = std::align_val_t(alignof(_Tp));
>>>+        auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp), __al);
>>>+        return { static_cast<_Tp*>(__p), __n };
>>>+      }
>>>+    else
>>>+      return __allocate_to_alignment<_Tp*>(__n, [](size_t __bytes)
>>>+       { return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__bytes)); });
>>>+      }
>>>+#endif
>>>+
>>>      // __p is not permitted to be a null pointer.
>>>      _GLIBCXX20_CONSTEXPR void
>>>      deallocate(_Tp* __p, size_type __n __attribute__ ((__unused__)))
>>>@@ -177,10 +214,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      {
>>>        _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n),
>>>                     std::align_val_t(alignof(_Tp)));
>>>-        return;
>>>      }
>>>+    else
>>>#endif
>>>-    _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
>>>+      _GLIBCXX_OPERATOR_DELETE(_GLIBCXX_SIZED_DEALLOC(__p, __n));
>>>      }
>>>
>>>#pragma GCC diagnostic pop
>>>diff --git a/libstdc++-v3/include/bits/stl_vector.h 
>>>b/libstdc++-v3/ include/bits/stl_vector.h
>>>index c4ca214752a..4efe3d0f9d2 100644
>>>--- a/libstdc++-v3/include/bits/stl_vector.h
>>>+++ b/libstdc++-v3/include/bits/stl_vector.h
>>>@@ -317,6 +317,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      get_allocator() const _GLIBCXX_NOEXCEPT
>>>      { return allocator_type(_M_get_Tp_allocator()); }
>>>
>>>+      static _GLIBCXX20_CONSTEXPR size_t
>>>+      _S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT
>>>+      {
>>>+    // std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,
>>>+    // and realistically we can't store more than PTRDIFF_MAX/sizeof(T)
>>>+    // (even if std::allocator_traits::max_size says we can).
>>>+    const size_t __diffmax =
>>>+      __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);
>>>+    const size_t __allocmax =
>>>+      __gnu_cxx::__alloc_traits<_Alloc>::max_size(__a);
>>>+    return (std::min)(__diffmax, __allocmax);
>>>+      }
>>>+
>>>#if __cplusplus >= 201103L
>>>      _Vector_base() = default;
>>>#else
>>>@@ -384,9 +397,37 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      _GLIBCXX20_CONSTEXPR
>>>      pointer
>>>      _M_allocate(size_t __n)
>>>+      { return _M_allocate_at_least(__n).__ptr; }
>>
>>This also does the rounding up work, then ignores it.
>
>ACK.
>
>
>>>+
>>>+      struct _Alloc_result { pointer __ptr; size_t __count; };
>>>+
>>>+      _GLIBCXX20_CONSTEXPR
>>>+      _Alloc_result
>>>+      _M_allocate_at_least(size_t __n)
>>>      {
>>>    typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
>>>-    return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
>>>+    _Alloc_result __r;
>>>+    if (__builtin_expect(__n != 0, true))
>>>+      {
>>>+#ifdef __glibcxx_allocate_at_least  // C++23
>>>+        if constexpr (
>>>+        requires(_Tp_alloc_type& __a, size_t __n) { 
>>>_Tr::allocate_at_least(__a, __n); })
>>>+          {
>>>+        auto [__ptr, __count] = _Tr::allocate_at_least(_M_impl, __n);
>>>+        if (__count > __n)
>>>+          {
>>>+            size_t __max = _S_max_size(_M_get_Tp_allocator());
>>>+            if (__builtin_expect(__count > __max, false))
>>>+              __count = __max;
>>>+          }
>>>+        __r.__ptr = __ptr, __r.__count = __count;
>>
>>Separate statements for the assignments please.
>
>ACK.
>
>
>>>+          }
>>>+        else
>>>+#endif
>>>+          __r.__ptr = _Tr::allocate(_M_impl, __n), __r.__count = __n;
>>>+      }
>>>+    else __r.__ptr = pointer(), __r.__count = 0;
>>
>>And again, although this line could be avoided by initializing __r
>>using {}.
>>
>>>+    return __r;
>>>      }
>>>
>>>      _GLIBCXX20_CONSTEXPR
>>>@@ -404,9 +445,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      void
>>>      _M_create_storage(size_t __n)
>>>      {
>>>-    this->_M_impl._M_start = this->_M_allocate(__n);
>>>-    this->_M_impl._M_finish = this->_M_impl._M_start;
>>>-    this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
>>>+    _Alloc_result __r = this->_M_allocate_at_least(__n);
>>>+    this->_M_impl._M_finish = this->_M_impl._M_start = __r.__ptr;
>>>+    this->_M_impl._M_end_of_storage = this->_M_impl._M_start + 
>>>__r.__count;
>>>      }
>>>
>>>#if __glibcxx_containers_ranges // C++ >= 23
>>>@@ -480,6 +521,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      typedef _Vector_base<_Tp, _Alloc>            _Base;
>>>      typedef typename _Base::_Tp_alloc_type        _Tp_alloc_type;
>>>      typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type>    _Alloc_traits;
>>>+      typedef typename _Base::_Alloc_result             _Alloc_result;
>>>
>>>    public:
>>>      typedef _Tp                    value_type;
>>>@@ -536,6 +578,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>
>>>    protected:
>>>      using _Base::_M_allocate;
>>>+      using _Base::_M_allocate_at_least;
>>>      using _Base::_M_deallocate;
>>>      using _Base::_M_impl;
>>>      using _Base::_M_get_Tp_allocator;
>>>@@ -1116,7 +1159,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
>>>      size_type
>>>      max_size() const _GLIBCXX_NOEXCEPT
>>>-      { return _S_max_size(_M_get_Tp_allocator()); }
>>>+      { return _Base::_S_max_size(_M_get_Tp_allocator()); }
>>>
>>>#if __cplusplus >= 201103L
>>>      /**
>>>@@ -1682,16 +1725,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          return;
>>>        }
>>>
>>>-          const size_type __len = _M_check_len(__n, 
>>>"vector::append_range");
>>>+          const size_type __ask = _M_check_len(__n, 
>>>"vector::append_range");
>>>
>>>          pointer __old_start = this->_M_impl._M_start;
>>>          pointer __old_finish = this->_M_impl._M_finish;
>>>
>>>-          allocator_type& __a = _M_get_Tp_allocator();
>>>-          const pointer __start = this->_M_allocate(__len);
>>>+          auto [__ptr, __got] = this->_M_allocate_at_least(__ask);
>>>+          const pointer __start = __ptr;
>>>          const pointer __mid = __start + __sz;
>>>          const pointer __back = __mid + __n;
>>>-          _Guard_alloc __guard(__start, __len, *this);
>>>+          _Guard_alloc __guard(__start, __got, *this);
>>>+          allocator_type& __a = _M_get_Tp_allocator();
>>>          std::__uninitialized_copy_a(ranges::begin(__rg),
>>>                      ranges::end(__rg),
>>>                      __mid, __a);
>>>@@ -1733,7 +1777,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          // Finally, take ownership of new storage:
>>>          this->_M_impl._M_start = __start;
>>>          this->_M_impl._M_finish = __back;
>>>-          this->_M_impl._M_end_of_storage = __start + __len;
>>>+          this->_M_impl._M_end_of_storage = __start + __got;
>>>        }
>>>      else
>>>        {
>>>@@ -1889,20 +1933,45 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>    protected:
>>>      /**
>>>       *  Memory expansion handler.  Uses the member allocation 
>>>function to
>>>-       *  obtain @a n bytes of memory, and then copies 
>>>[first,last) into it.
>>>+       *  obtain at least `n` objects worth of memory, copies 
>>>`[first,last)`
>>>+       *  into it, reports what was actually allocated.
>>>       */
>>>      template<typename _ForwardIterator>
>>>    _GLIBCXX20_CONSTEXPR
>>>-    pointer
>>>-    _M_allocate_and_copy(size_type __n,
>>>-                 _ForwardIterator __first, _ForwardIterator __last)
>>>+    _Alloc_result
>>>+    _M_allocate_and_copy(
>>>+      size_type __n, _ForwardIterator __first, _ForwardIterator __last)
>>
>>The parameter formatting was correct before this change.
>
>ACK.
>
>
>>>    {
>>>-      _Guard_alloc __guard(this->_M_allocate(__n), __n, *this);
>>>+      _Alloc_result __r = this->_M_allocate_at_least(__n);
>>>+      _Guard_alloc __guard(__r.__ptr, __r.__count, *this);
>>>      std::__uninitialized_copy_a
>>>        (__first, __last, __guard._M_storage, _M_get_Tp_allocator());
>>>-      return __guard._M_release();
>>>+      (void) __guard._M_release();
>>>+      return __r;
>>>    }
>>>
>>>+      _GLIBCXX20_CONSTEXPR void
>>>+      _M_displace_storage(pointer __start, pointer __end, 
>>>size_type __cap)
>>
>>Would _M_replace_storage be a better name?
>>
>>"displace" has three meanings, one of which is to take the place of
>>something else (the intended meaning here, where the new storage
>>replaces the old), but another meaning is to just push something out
>>of its place. So at the call site, if I read "displace storage" I
>>don't know what it's doing.
>
>ACK.
>
>
>>>+      {
>>>+      _GLIBCXX_ASAN_ANNOTATE_REINIT;
>>>+      _M_deallocate(this->_M_impl._M_start,
>>>+        this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
>>>+      this->_M_impl._M_start = __start;
>>>+      this->_M_impl._M_finish = __end;
>>>+      this->_M_impl._M_end_of_storage = __start + __cap;
>>>+      }
>>>+
>>>+      template<typename _ForwardIterator>
>>>+    _GLIBCXX20_CONSTEXPR
>>>+    void
>>>+    _M_allocate_and_migrate(
>>
>>This name doesn't tell me what it does either, "migrate" doesn't tell
>>me that it makes a copy of [first, last) rather than migrating
>>something *to* that range.
>>
>>Would _M_replace_with be better? The fact it allocates isn't really
>>the more pertinent thing about this function, what it does is replace
>>the current storage with new storage containing a copy of the range
>>[first, last).
>
>ACK.
>
>
>>>+      size_type __n, _ForwardIterator __first, _ForwardIterator __last)
>>
>>Incorrect formatting again.
>
>ACK.
>
>
>>>+    {
>>>+      _Alloc_result __r = _M_allocate_and_copy(__n, __first, __last);
>>>+      std::_Destroy(this->_M_impl._M_start,
>>>+        this->_M_impl._M_finish, _M_get_Tp_allocator());
>>>+      _M_displace_storage(__r.__ptr, __r.__ptr + __n, __r.__count);
>>>+    }
>>>
>>>      // Internal constructor functions follow.
>>>
>>>@@ -1971,10 +2040,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>    _M_range_initialize_n(_Iterator __first, _Sentinel __last,
>>>                  size_pointer type.
>type __n)
>>>    {
>>>-      pointer __start =
>>>-        this->_M_allocate(_S_check_init_len(__n, 
>>>_M_get_Tp_allocator()));
>>>+      _Alloc_result __r = this->_M_allocate_at_least(
>>>+        _S_check_init_len(__n, _M_get_Tp_allocator()));
>>>+      pointer __start = __r.__ptr;
>>>      this->_M_impl._M_start = this->_M_impl._M_finish = __start;
>>>-      this->_M_impl._M_end_of_storage = __start + __n;
>>>+      this->_M_impl._M_end_of_storage = __start + __r.__count;
>>>      this->_M_impl._M_finish
>>>          = std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last,
>>>                        __start, _M_get_Tp_allocator());
>>>@@ -2191,35 +2261,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      size_type
>>>      _M_check_len(size_type __n, const char* __s) const
>>>      {
>>>-    if (max_size() - size() < __n)
>>>+    const size_type __room = max_size() - size();
>>>+    if (__room < __n)
>>>      __throw_length_error(__N(__s));
>>>
>>>-    const size_type __len = size() + (std::max)(size(), __n);
>>>-    return (__len < size() || __len > max_size()) ? max_size() : __len;
>>>+    if (__n < size())
>>>+      __n = size();  // Grow by (at least) doubling ...
>>>+    if (__n > __room)
>>>+      __n = __room;  //  ... but only as much as will fit.
>>>+    return size() + __n;
>>>      }
>>>
>>>      // Called by constructors to check initial size.
>>>      static _GLIBCXX20_CONSTEXPR size_type
>>>      _S_check_init_len(size_type __n, const allocator_type& __a)
>>>      {
>>>-    if (__n > _S_max_size(_Tp_alloc_type(__a)))
>>>+    if (__n > _Base::_S_max_size(_Tp_alloc_type(__a)))
>>>      __throw_length_error(
>>>          __N("cannot create std::vector larger than max_size()"));
>>>    return __n;
>>>      }
>>>
>>>-      static _GLIBCXX20_CONSTEXPR size_type
>>>-      _S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT
>>>-      {
>>>-    // std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,
>>>-    // and realistically we can't store more than PTRDIFF_MAX/sizeof(T)
>>>-    // (even if std::allocator_traits::max_size says we can).
>>>-    const size_t __diffmax
>>>-      = __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);
>>>-    const size_t __allocmax = _Alloc_traits::max_size(__a);
>>>-    return (std::min)(__diffmax, __allocmax);
>>>-      }
>>>-
>>>      // Internal erase functions follow.
>>>
>>>      // Called by erase(q1,q2), clear(), resize(), _M_fill_assign,
>>>diff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/ 
>>>include/bits/vector.tcc
>>>index b790fca2964..86da54bdff1 100644
>>>--- a/libstdc++-v3/include/bits/vector.tcc
>>>+++ b/libstdc++-v3/include/bits/vector.tcc
>>>@@ -75,13 +75,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      if (this->capacity() < __n)
>>>    {
>>>      const size_type __old_size = size();
>>>-      pointer __tmp;
>>>+      _Alloc_result __tmp;
>>>#if __cplusplus >= 201103L
>>>      if constexpr (_S_use_relocate())
>>>        {
>>>-          __tmp = this->_M_allocate(__n);
>>>+          __tmp = this->_M_allocate_at_least(__n);
>>>          std::__relocate_a(this->_M_impl._M_start, this- 
>>>>_M_impl._M_finish,
>>>-                __tmp, _M_get_Tp_allocator());
>>>+                __tmp.__ptr, _M_get_Tp_allocator());
>>>        }
>>>      else
>>>#endif
>>>@@ -92,13 +92,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
>>>                _M_get_Tp_allocator());
>>>        }
>>>-      _GLIBCXX_ASAN_ANNOTATE_REINIT;
>>>-      _M_deallocate(this->_M_impl._M_start,
>>>-            this->_M_impl._M_end_of_storage
>>>-            - this->_M_impl._M_start);
>>>-      this->_M_impl._M_start = __tmp;
>>>-      this->_M_impl._M_finish = __tmp + __old_size;
>>>-      this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
>>>+      _M_displace_storage(
>>>+        __tmp.__ptr, __tmp.__ptr + __old_size, __tmp.__count);
>>>    }
>>>    }
>>>#pragma GCC diagnostic pop
>>>@@ -106,12 +101,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>#if __cplusplus >= 201103L
>>>  template<typename _Tp, typename _Alloc>
>>>    template<typename... _Args>
>>>-#if __cplusplus > 201402L
>>>+# if __cplusplus > 201402L
>>>      _GLIBCXX20_CONSTEXPR
>>>      typename vector<_Tp, _Alloc>::reference
>>>-#else
>>>+# else
>>>      void
>>>-#endif
>>>+# endif
>>>      vector<_Tp, _Alloc>::
>>>      emplace_back(_Args&&... __args)
>>>      {
>>>@@ -125,11 +120,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      }
>>>    else
>>>      _M_realloc_append(std::forward<_Args>(__args)...);
>>>-#if __cplusplus > 201402L
>>>+# if __cplusplus > 201402L
>>>    return back();
>>>-#endif
>>>+# endif
>>>      }
>>>-#endif
>>>+#endif  // __cplusplus >= 201103L
>>>
>>>  template<typename _Tp, typename _Alloc>
>>>    _GLIBCXX20_CONSTEXPR
>>>@@ -238,22 +233,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>#endif
>>>      const size_type __xlen = __x.size();
>>>      if (__xlen > capacity())
>>>-        {
>>>-          pointer __tmp = _M_allocate_and_copy(__xlen, __x.begin(),
>>>-                           __x.end());
>>>-          std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
>>>-                _M_get_Tp_allocator());
>>>-          _M_deallocate(this->_M_impl._M_start,
>>>-                this->_M_impl._M_end_of_storage
>>>-                - this->_M_impl._M_start);
>>>-          this->_M_impl._M_start = __tmp;
>>>-          this->_M_impl._M_end_of_storage = 
>>>this->_M_impl._M_start + __xlen;
>>>-        }
>>>+        _M_allocate_and_migrate(__xlen, __x.begin(), __x.end());
>>>      else if (size() >= __xlen)
>>>-        {
>>>-          std::_Destroy(std::copy(__x.begin(), __x.end(), begin()),
>>>-                end(), _M_get_Tp_allocator());
>>>-        }
>>>+        std::_Destroy(std::copy(__x.begin(), __x.end(), begin()),
>>>+          end(), _M_get_Tp_allocator());
>>>      else
>>>        {
>>>          std::copy(__x._M_impl._M_start, __x._M_impl._M_start + size(),
>>>@@ -332,16 +315,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          __builtin_unreachable();
>>>
>>>        _S_check_init_len(__len, _M_get_Tp_allocator());
>>>-        pointer __tmp(_M_allocate_and_copy(__len, __first, __last));
>>>-        std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
>>>-              _M_get_Tp_allocator());
>>>-        _GLIBCXX_ASAN_ANNOTATE_REINIT;
>>>-        _M_deallocate(this->_M_impl._M_start,
>>>-              this->_M_impl._M_end_of_storage
>>>-              - this->_M_impl._M_start);
>>>-        this->_M_impl._M_start = __tmp;
>>>-        this->_M_impl._M_finish = this->_M_impl._M_start + __len;
>>>-        this->_M_impl._M_end_of_storage = this->_M_impl._M_finish;
>>>+        _M_allocate_and_migrate(__len, __first, __last);
>>>      }
>>>    else if (__sz >= __len)
>>>      _M_erase_at_end(std::copy(__first, __last, this- 
>>>>_M_impl._M_start));
>>>@@ -464,13 +438,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>    _M_realloc_insert(iterator __position, const _Tp& __x)
>>>#endif
>>>    {
>>>-      const size_type __len = _M_check_len(1u, 
>>>"vector::_M_realloc_insert");
>>>-      if (__len <= 0)
>>>+      const size_type __len1 = _M_check_len(1u, 
>>>"vector::_M_realloc_insert");
>>>+      if (__len1 <= 0)
>>>    __builtin_unreachable();
>>>      pointer __old_start = this->_M_impl._M_start;
>>>      pointer __old_finish = this->_M_impl._M_finish;
>>>      const size_type __elems_before = __position - begin();
>>>-      pointer __new_start(this->_M_allocate(__len));
>>>+      _Alloc_result __r = this->_M_allocate_at_least(__len1);
>>>+      const size_type __len = __r.__count;
>>>+      pointer __new_start(__r.__ptr);
>>>      pointer __new_finish(__new_start);
>>>
>>>      {
>>>@@ -574,14 +550,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>      const size_type __len = _M_check_len(1u, 
>>>"vector::_M_realloc_append");
>>>      if (__len <= 0)
>>>    __builtin_unreachable();
>>>-      pointer __old_start = this->_M_impl._M_start;
>>>-      pointer __old_finish = this->_M_impl._M_finish;
>>>+      const pointer __old_start = this->_M_impl._M_start;
>>>+      const pointer __old_finish = this->_M_impl._M_finish;
>>>      const size_type __elems = size();
>>>-      pointer __new_start(this->_M_allocate(__len));
>>>+      const _Alloc_result __r = this->_M_allocate_at_least(__len);
>>>+      const size_type __rlen = __r.__count;
>>>+      const pointer __new_start(__r.__ptr);
>>>      pointer __new_finish(__new_start);
>>>
>>>      {
>>>-    _Guard_alloc __guard(__new_start, __len, *this);
>>>+    _Guard_alloc __guard(__new_start, __rlen, *this);
>>>
>>>    // The order of the three operations is dictated by the C++11
>>>    // case, where the moves could alter a new element belonging
>>>@@ -652,7 +630,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>
>>>      this->_M_impl._M_start = __new_start;
>>>      this->_M_impl._M_finish = __new_finish;
>>>-      this->_M_impl._M_end_of_storage = __new_start + __len;
>>>+      this->_M_impl._M_end_of_storage = __new_start + __rlen;
>>>    }
>>>#pragma GCC diagnostic pop
>>>
>>>@@ -716,10 +694,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          pointer __old_finish = this->_M_impl._M_finish;
>>>          const pointer __pos = __position.base();
>>>
>>>-          const size_type __len =
>>>+          const size_type __len1 =
>>>        _M_check_len(__n, "vector::_M_fill_insert");
>>>          const size_type __elems_before = __pos - __old_start;
>>>-          pointer __new_start(this->_M_allocate(__len));
>>>+          _Alloc_result __r = this->_M_allocate_at_least(__len1);
>>>+          const size_type __len = __r.__count;
>>>+          pointer __new_start(__r.__ptr);
>>>          pointer __new_finish(__new_start);
>>>          __try
>>>        {
>>>@@ -727,21 +707,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          std::__uninitialized_fill_n_a(__new_start + __elems_before,
>>>                        __n, __x,
>>>                        _M_get_Tp_allocator());
>>>-          __new_finish = pointer();
>>>-
>>>-          __new_finish
>>>-            = std::__uninitialized_move_if_noexcept_a
>>>-            (__old_start, __pos, __new_start, _M_get_Tp_allocator());
>>>-
>>>+          __new_finish = pointer();  // ... in case of a throw.
>>>+          __new_finish = std::__uninitialized_move_if_noexcept_a(
>>>+              __old_start, __pos, __new_start, _M_get_Tp_allocator());
>>>          __new_finish += __n;
>>>-
>>>-          __new_finish
>>>-            = std::__uninitialized_move_if_noexcept_a
>>>-            (__pos, __old_finish, __new_finish, _M_get_Tp_allocator());
>>>+          __new_finish = std::__uninitialized_move_if_noexcept_a(
>>>+              __pos, __old_finish, __new_finish, _M_get_Tp_allocator());
>>>        }
>>>          __catch(...)
>>>        {
>>>-          if (!__new_finish)
>>>+          if (__new_finish == pointer())
>>>            std::_Destroy(__new_start + __elems_before,
>>>                  __new_start + __elems_before + __n,
>>>                  _M_get_Tp_allocator());
>>>@@ -752,12 +727,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          __throw_exception_again;
>>>        }
>>>          std::_Destroy(__old_start, __old_finish, 
>>>_M_get_Tp_allocator());
>>>-          _GLIBCXX_ASAN_ANNOTATE_REINIT;
>>>-          _M_deallocate(__old_start,
>>>-                this->_M_impl._M_end_of_storage - __old_start);
>>>-          this->_M_impl._M_start = __new_start;
>>>-          this->_M_impl._M_finish = __new_finish;
>>>-          this->_M_impl._M_end_of_storage = __new_start + __len;
>>>+          _M_displace_storage(__new_start, __new_finish, __len);
>>>        }
>>>    }
>>>    }
>>>@@ -785,9 +755,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>       pointer __old_finish = this->_M_impl._M_finish;
>>>       const size_type __old_size = __old_finish - __old_start;
>>>
>>>-       const size_type __len =
>>>-         _M_check_len(__n, "vector::_M_fill_append");
>>>-       pointer __new_start(this->_M_allocate(__len));
>>>+       size_type __len = _M_check_len(__n, "vector::_M_fill_append");
>>>+       _Alloc_result __r = this->_M_allocate_at_least(__len);
>>>+       __len = __r.__count;
>>>+       pointer __new_start(__r.__ptr);
>>>       pointer __new_finish(__new_start + __old_size);
>>>       __try
>>>         {
>>>@@ -807,12 +778,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>        __throw_exception_again;
>>>          }
>>>       std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator());
>>>-       _GLIBCXX_ASAN_ANNOTATE_REINIT;
>>>-       _M_deallocate(__old_start,
>>>-             this->_M_impl._M_end_of_storage - __old_start);
>>>-       this->_M_impl._M_start = __new_start;
>>>-       this->_M_impl._M_finish = __new_finish;
>>>-       this->_M_impl._M_end_of_storage = __new_start + __len;
>>>+       _M_displace_storage(__new_start, __new_finish, __len);
>>>     }
>>>    }
>>>
>>>@@ -852,9 +818,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          pointer __old_start = this->_M_impl._M_start;
>>>          pointer __old_finish = this->_M_impl._M_finish;
>>>
>>>-          const size_type __len =
>>>+          const size_type __len1 =
>>>        _M_check_len(__n, "vector::_M_default_append");
>>>-          pointer __new_start(this->_M_allocate(__len));
>>>+          _Alloc_result __r = this->_M_allocate_at_least(__len1);
>>>+          const size_type __len = __r.__count;
>>>+          pointer __new_start(__r.__ptr);
>>>
>>>          {
>>>        _Guard_alloc __guard(__new_start, __len, *this);
>>>@@ -1003,14 +971,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>        pointer __old_start = this->_M_impl._M_start;
>>>        pointer __old_finish = this->_M_impl._M_finish;
>>>
>>>-        const size_type __len =
>>>+        const size_type __ask =
>>>          _M_check_len(__n, "vector::_M_range_insert");
>>>#if __cplusplus < 201103L
>>>-        if (__len < (__n + (__old_finish - __old_start)))
>>>+        if (__ask < (__n + (__old_finish - __old_start)))
>>>          __builtin_unreachable();
>>>#endif
>>>
>>>-        pointer __new_start(this->_M_allocate(__len));
>>>+        _Alloc_result __r = this->_M_allocate_at_least(__ask);
>>>+        const size_type __got = __r.__count;
>>>+        pointer __new_start(__r.__ptr);
>>>        pointer __new_finish(__new_start);
>>>        __try
>>>          {
>>>@@ -1031,17 +1001,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          {
>>>            std::_Destroy(__new_start, __new_finish,
>>>                  _M_get_Tp_allocator());
>>>-            _M_deallocate(__new_start, __len);
>>>+            _M_deallocate(__new_start, __got);
>>>            __throw_exception_again;
>>>          }
>>>        std::_Destroy(__old_start, __old_finish,
>>>                  _M_get_Tp_allocator());
>>>-        _GLIBCXX_ASAN_ANNOTATE_REINIT;
>>>-        _M_deallocate(__old_start,
>>>-                  this->_M_impl._M_end_of_storage - __old_start);
>>>-        this->_M_impl._M_start = __new_start;
>>>-        this->_M_impl._M_finish = __new_finish;
>>>-        this->_M_impl._M_end_of_storage = __new_start + __len;
>>>+        _M_displace_storage(__new_start, __new_finish, __got);
>>>          }
>>>      }
>>>      }
>>>@@ -1111,7 +1076,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>          }
>>>        else // Reallocate
>>>          {
>>>-        const size_type __len
>>>+        const size_type __ask
>>>          = _M_check_len(__n, "vector::insert_range");
>>>
>>>        struct _Guard : _Guard_alloc
>>>@@ -1130,8 +1095,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>        };
>>>
>>>        // Allocate new storage:
>>>-        pointer __new_start(this->_M_allocate(__len));
>>>-        _Guard __guard(__new_start, __len, *this);
>>>+        _Alloc_result __r = this->_M_allocate_at_least(__ask);
>>>+        const size_type __got = __r.__count;
>>>+        pointer __new_start(__r.__ptr);
>>>+        _Guard __guard(__new_start, __got, *this);
>>>
>>>        auto& __alloc = _M_get_Tp_allocator();
>>>
>>>@@ -1158,13 +1125,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>>>        // with __guard so that it cleans up the old storage:
>>>        this->_M_impl._M_start = __guard._M_storage;
>>>        this->_M_impl._M_finish = __guard._M_finish;
>>>-        this->_M_impl._M_end_of_storage = __new_start + __len;
>>>+        this->_M_impl._M_end_of_storage = __new_start + __got;
>>>        __guard._M_storage = __old_start;
>>>        __guard._M_finish = __old_finish;
>>>        __guard._M_len = (__old_finish - __old_start) + __cap;
>>>        // _Asan::_Reinit destructor marks unused capacity.
>>>        // _Guard destructor destroys [old_start,old_finish).
>>>-        // _Guard_alloc destructor frees [old_start,old_start+len).
>>>+        // _Guard_alloc destructor frees [old_start,old_start+got).
>>>          }
>>>        return begin() + __ins_idx;
>>>      }
>>>diff --git a/libstdc++-v3/include/std/string 
>>>b/libstdc++-v3/include/ std/string
>>>index c2b37391fc7..da50bce2e7d 100644
>>>--- a/libstdc++-v3/include/std/string
>>>+++ b/libstdc++-v3/include/std/string
>>>@@ -67,6 +67,7 @@
>>>#endif
>>>
>>>#define __glibcxx_want_algorithm_default_value_type
>>>+#define __glibcxx_want_allocate_at_least
>>
>>This macro should only defined in <memory> and <version>, not here.
>
>ACK. I guess the specs for <string> and <vector> don't mention
>allocate_at_least, making it an implemention detail?

Right



More information about the Libstdc++ mailing list