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

Nathan Myers ncm@cantrip.org
Wed Apr 15 12:52:49 GMT 2026


Anticipating a comment... given an allocation request 2^62 - n
n < 16, this code would round it up to 2^62, which the compiler
has figured out and noted a la PR108377.

On 4/15/26 1:34 AM, Nathan Myers wrote:
> [This patch has been unusually provocative of compiler bugs,
> particularly of the PR108377 "exceeds maximum object size"
> variety that may be seen worked around in the lambda passed to
> _S_allocate_to_alignment(), and tests that fail with -O2 but
> pass with -O1 and -O3, some of which remain.]
> 
> 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.
> 
> 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 (_S_allocate_to_alignment): Define.
> 	(allocate_at_least): Call __a.allocate_at_least iff it exists.
> 	* include/bits/allocator.h (allocate_at_least): Delegate to base
> 	allocate_at_least where defined, calling with explicit base-class
> 	qualification.
> 	* include/bits/new_allocator.h (allocate_at_least): Define, using
> 	_S_allocate_to_alignment from alloc_traits.h.
> 	* include/bits/basic_string.h (_Alloc_result): Define.
> 	(_S_allocate_at_least): Define.
> 	(_S_allocate): Delegate to _S_allocate_at_least.
> 	(assign): Use _S_allocate_at_least.
> 	* include/bits/basic_string.tcc (_M_create_plus): Define.
> 	(_M_replace, reserve): Use _S_allocate_at_least.
> 	(_M_create, _M_construct (4x), _M_assign, reserve, _M_mutate):
> 	Use _M_create_plus.
> 	* include/bits/memory_resource.h (allocate_at_least): Define,
> 	document.
> 	* include/bits/stl_vector.h (_S_max_size): Move to _Vector_base.
> 	(_Alloc_result): Define.
> 	(_M_allocate_at_least): Define.
> 	(_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_range_initialize): Use _M_allocate_at_least.
> 	(_M_check_len): Improve logic.
> 	* include/bits/vector.tcc (reserve, _M_realloc_insert,
> 	_M_realloc_append, _M_fill_insert, _M_fill_append, _M_default_append,
> 	_M_range_insert, insert_range): Use _M_allocate_at_least.
> 	* 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 (extra): Define,
> 	with new tests.
> 	* testsuite/21_strings/basic_string/capacity/char/18654.cc
> 	(test01): Loosen capacity check.
> 	* testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc
> 	(test01): Same.
> 	* testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc
> 	(test01): Same.
> 	* testsuite/21_strings/basic_string/capacity/wchar_t/2.cc
> 	(test02): Same.
> 	* testsuite/21_strings/basic_string/capacity/wchar_t/shrink_to_fit.cc
> 	(test01): Same.
> 	* testsuite/23_containers/vector/capacity/shrink_to_fit.cc
> 	(test01): Same.
> 	* testsuite/23_containers/vector/capacity/shrink_to_fit2.cc
> 	(test01): Same.
> 	* testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc
> 	(test03, test04): Adapt to looser reserve behavior.
> 	* config/abi/pre/gnu.ver: expose symbols string::_S_allocate_at_least,
> 	_M_create_plus.
> ---
>   libstdc++-v3/config/abi/pre/gnu.ver           |   4 +
>   libstdc++-v3/include/bits/alloc_traits.h      |  43 +++++--
>   libstdc++-v3/include/bits/allocator.h         |  27 +++--
>   libstdc++-v3/include/bits/basic_string.h      |  31 +++--
>   libstdc++-v3/include/bits/basic_string.tcc    |  73 +++++++-----
>   libstdc++-v3/include/bits/memory_resource.h   |  21 ++++
>   libstdc++-v3/include/bits/new_allocator.h     |  38 ++++++
>   libstdc++-v3/include/bits/stl_vector.h        |  91 ++++++++++-----
>   libstdc++-v3/include/bits/vector.tcc          |  69 ++++++-----
>   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       |   3 +-
>   .../capacity/char/shrink_to_fit.cc            |   3 +-
>   .../basic_string/capacity/wchar_t/18654.cc    |   4 +-
>   .../basic_string/capacity/wchar_t/2.cc        |   9 +-
>   .../capacity/wchar_t/shrink_to_fit.cc         |   3 +-
>   .../vector/capacity/shrink_to_fit.cc          |   7 +-
>   .../vector/capacity/shrink_to_fit2.cc         |   6 +-
>   .../vector/modifiers/emplace/self_emplace.cc  |  28 ++---
>   .../testsuite/util/testsuite_allocator.h      |  35 ++++++
>   21 files changed, 465 insertions(+), 140 deletions(-)
> 
> diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
> index 624de951d41..a495cb06de7 100644
> --- a/libstdc++-v3/config/abi/pre/gnu.ver
> +++ b/libstdc++-v3/config/abi/pre/gnu.ver
> @@ -2606,6 +2606,10 @@ 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, _M_create_plus
> +    _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE11_S_allocate_*;
> +    _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE11_M_create_plus*;
> +
>   #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..7851b6d919e 100644
> --- a/libstdc++-v3/include/bits/alloc_traits.h
> +++ b/libstdc++-v3/include/bits/alloc_traits.h
> @@ -419,7 +419,7 @@ _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);
> @@ -664,15 +664,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)
> +      [[nodiscard,__gnu__::__always_inline__]] static constexpr auto
> +      allocate_at_least(allocator_type& __a, size_type __n)
>   	-> allocation_result<pointer, size_type>
> -      { return __a.allocate_at_least(__n); }
> +      {
> +	if constexpr (requires
> +	       { __a.allocate_at_least(__n); })
> +	  return __a.allocate_at_least(__n);
> +	else
> +	  return { __a.allocate(__n), __n };
> +      }
>   #endif
>   
>         /**
> @@ -874,6 +881,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       };
>   #endif
>   
> +#ifdef __glibcxx_allocate_at_least
> +  template <typename _Tp, typename _Alloc_fn>
> +      [[nodiscard]] constexpr auto
> +      _S_allocate_to_alignment(size_t __n, _Alloc_fn&& __af)
> +      -> allocation_result<_Tp*, size_t>
> +    {
> +      const size_t __align_mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
> +      if constexpr (sizeof(_Tp) > __align_mask)
> +	return { static_cast<_Tp*>(__af(__n * sizeof(_Tp))), __n };
> +      else
> +	{
> +	  const size_t __need = __n * sizeof(_Tp);
> +	  const size_t __ask = (__need + __align_mask) & ~__align_mask;
> +	  using _U8 = const unsigned char;
> +	  static_assert(sizeof(_Tp) <= _U8(-1));
> +	  // Use 8-bit division:
> +	  _U8 __spare = __ask - __need, __size = sizeof(_Tp);
> +	  return { static_cast<_Tp*>(__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..59c8c8a0d94 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,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   
>   	return __allocator_base<_Tp>::allocate(__n, 0);
>         }
> +#endif
> +
> +#ifdef __glibcxx_allocate_at_least  // C++23
> +      [[nodiscard,__gnu__::__always_inline__]]
> +      constexpr allocation_result<_Tp*, size_t>
> +      allocate_at_least(size_t __n)
> +      {
> +	if (std::is_constant_evaluated())
> +	  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 };
> +      }
> +#endif
>   
> +#if __cpp_constexpr_dynamic_alloc // >= C++20
>         [[__gnu__::__always_inline__]]
>         constexpr void
>         deallocate(_Tp* __p, size_t __n)
> @@ -219,12 +236,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..ecf96f68c9e 100644
> --- a/libstdc++-v3/include/bits/basic_string.h
> +++ b/libstdc++-v3/include/bits/basic_string.h
> @@ -135,20 +135,33 @@ _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; }
> +
> +      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
> @@ -290,6 +303,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>         pointer
>         _M_create(size_type&, size_type);
>   
> +      _GLIBCXX20_CONSTEXPR
> +      _Alloc_result
> +      _M_create_plus(size_type&, size_type);
> +
>         _GLIBCXX20_CONSTEXPR
>         void
>         _M_dispose()
> @@ -1782,10 +1799,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..5b8d589f60e 100644
> --- a/libstdc++-v3/include/bits/basic_string.tcc
> +++ b/libstdc++-v3/include/bits/basic_string.tcc
> @@ -137,16 +137,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         __s._M_length(__tmp_length);
>       }
>   
> +  // For ABI reasons this must remain, though unused in active 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)
> +    { return _M_create_plus(__capacity, __old_capacity).__ptr; }
> +
> +  template<typename _CharT, typename _Traits, typename _Alloc>
> +    _GLIBCXX20_CONSTEXPR
> +    typename basic_string<_CharT, _Traits, _Alloc>::_Alloc_result
> +    basic_string<_CharT, _Traits, _Alloc>::
> +    _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 +169,7 @@ _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);
>       }
>   
>     // NB: This is the special case for Input Iterators, used in
> @@ -204,11 +212,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   	      {
>   		// 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(__capacity, __len);
> +		this->_S_copy(__another.__ptr, _M_data(), __len);
>   		_M_dispose();
> -		_M_data(__another);
> -		_M_capacity(__capacity);
> +		_M_data(__another.__ptr);
> +		_M_capacity(__another.__count - 1);
>   	      }
>   	    traits_type::assign(_M_data()[__len++],
>   				static_cast<_CharT>(*__beg));
> @@ -232,8 +240,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   
>   	if (__dnew > size_type(_S_local_capacity))
>   	  {
> -	    _M_data(_M_create(__dnew, size_type(0)));
> -	    _M_capacity(__dnew);
> +	    _Alloc_result __r = _M_create_plus(__dnew, size_type(0));
> +	    _M_data(__r.__ptr);
> +	    _M_capacity(__r.__count - 1);
>   	  }
>   	else
>   	  _M_init_local_buf();
> @@ -265,8 +274,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       {
>         if (__n > size_type(_S_local_capacity))
>   	{
> -	  _M_data(_M_create(__n, size_type(0)));
> -	  _M_capacity(__n);
> +	  _Alloc_result __r = _M_create_plus(__n, size_type(0));
> +	  _M_data(__r.__ptr);
> +	  _M_capacity(__r.__count - 1);
>   	}
>         else
>   	_M_init_local_buf();
> @@ -288,8 +298,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       {
>         if (__n > size_type(_S_local_capacity))
>   	{
> -	  _M_data(_M_create(__n, size_type(0)));
> -	  _M_capacity(__n);
> +	  _Alloc_result __r = _M_create_plus(__n, size_type(0));
> +	  _M_data(__r.__ptr);
> +	  _M_capacity(__r.__count - 1);
>   	}
>         else
>   	_M_init_local_buf();
> @@ -348,10 +359,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   	  if (__rsize > __capacity)
>   	    {
>   	      size_type __new_capacity = __rsize;
> -	      pointer __tmp = _M_create(__new_capacity, __capacity);
> +	      _Alloc_result __r = _M_create_plus(__new_capacity, size_type(0));
>   	      _M_dispose();
> -	      _M_data(__tmp);
> -	      _M_capacity(__new_capacity);
> +	      _M_data(__r.__ptr);
> +	      _M_capacity(__r.__count - 1);
>   	    }
>   
>   	  if (__rsize)
> @@ -375,11 +386,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 __tmp = _M_create_plus(__res, __capacity);
> +      this->_S_copy(__tmp.__ptr, _M_data(), length() + 1);
>         _M_dispose();
> -      _M_data(__tmp);
> -      _M_capacity(__res);
> +      _M_data(__tmp.__ptr);
> +      _M_capacity(__tmp.__count - 1);
>       }
>   
>     template<typename _CharT, typename _Traits, typename _Alloc>
> @@ -392,19 +403,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);
>       }
>   
>     template<typename _CharT, typename _Traits, typename _Alloc>
> @@ -444,11 +455,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         else if (__length < __capacity)
>   	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 +600,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;
>   	      _S_copy(__newp, this->_M_data(), __pos);
>   	      _S_copy(__newp + __pos, __s, __len2);
>   	      _S_copy(__newp + __pos + __len2, __p + __len1, __how_much);
> diff --git a/libstdc++-v3/include/bits/memory_resource.h b/libstdc++-v3/include/bits/memory_resource.h
> index e5c6697b07e..105945d435a 100644
> --- a/libstdc++-v3/include/bits/memory_resource.h
> +++ b/libstdc++-v3/include/bits/memory_resource.h
> @@ -468,6 +468,27 @@ 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); })
> +	  return __a.allocate_at_least(__n);
> +	else return { __a.allocate(__n), __n };
> +      }
> +#endif
> +
>         /**
>          *  @brief  Deallocate memory.
>          *  @param  __a  An allocator.
> diff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/include/bits/new_allocator.h
> index fbe03e392aa..fd910300579 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,42 @@ _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())
> +	  {
> +	    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 _S_allocate_to_alignment<_Tp>(__n,
> +	  [](size_t __bytes) {
> +	    if (__bytes > ~size_t() >> 1) __builtin_unreachable();
> +	    return _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__)))
> diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h
> index c4ca214752a..cec75e10089 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,33 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>         _GLIBCXX20_CONSTEXPR
>         pointer
>         _M_allocate(size_t __n)
> +      { return _M_allocate_at_least(__n).__ptr; }
> +
> +      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
> +	    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;
> +#else
> +	    __r.__ptr = _Tr::allocate(_M_impl, __n), __r.__count = __n;
> +#endif
> +	  }
> +	else __r.__ptr = pointer(), __r.__count = 0;
> +	return __r;
>         }
>   
>         _GLIBCXX20_CONSTEXPR
> @@ -404,9 +441,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 +517,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 +574,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 +1155,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 +1721,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>   		  return;
>   		}
>   
> -	      const size_type __len = _M_check_len(__n, "vector::append_range");
> +	      const size_type __len1 = _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, __count] = this->_M_allocate_at_least(__len1);
> +	      const size_type __len = __count;
> +	      const pointer __start = __ptr;
>   	      const pointer __mid = __start + __sz;
>   	      const pointer __back = __mid + __n;
>   	      _Guard_alloc __guard(__start, __len, *this);
> +	      allocator_type& __a = _M_get_Tp_allocator();
>   	      std::__uninitialized_copy_a(ranges::begin(__rg),
>   					  ranges::end(__rg),
>   					  __mid, __a);
> @@ -1897,7 +1938,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>   	_M_allocate_and_copy(size_type __n,
>   			     _ForwardIterator __first, _ForwardIterator __last)
>   	{
> -	  _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();
> @@ -1971,10 +2013,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>   	_M_range_initialize_n(_Iterator __first, _Sentinel __last,
>   			      size_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 +2234,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..5e7ec11fa6f 100644
> --- a/libstdc++-v3/include/bits/vector.tcc
> +++ b/libstdc++-v3/include/bits/vector.tcc
> @@ -79,7 +79,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>   #if __cplusplus >= 201103L
>   	  if constexpr (_S_use_relocate())
>   	    {
> -	      __tmp = this->_M_allocate(__n);
> +	      auto __res = this->_M_allocate_at_least(__n);
> +	      __tmp = __res.__ptr;
> +	      __n = __res.__count;
>   	      std::__relocate_a(this->_M_impl._M_start, this->_M_impl._M_finish,
>   				__tmp, _M_get_Tp_allocator());
>   	    }
> @@ -106,12 +108,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 +127,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
> @@ -464,13 +466,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 +578,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 +658,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 +722,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
>   		{
> @@ -785,9 +793,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
>   	     {
> @@ -852,9 +861,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 +1014,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 __len1 =
>   		  _M_check_len(__n, "vector::_M_range_insert");
>   #if __cplusplus < 201103L
> -		if (__len < (__n + (__old_finish - __old_start)))
> +		if (__len1 < (__n + (__old_finish - __old_start)))
>   		  __builtin_unreachable();
>   #endif
>   
> -		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
>   		  {
> @@ -1111,7 +1124,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>   	      }
>   	    else // Reallocate
>   	      {
> -		const size_type __len
> +		const size_type __len1
>   		  = _M_check_len(__n, "vector::insert_range");
>   
>   		struct _Guard : _Guard_alloc
> @@ -1130,7 +1143,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
>   		};
>   
>   		// Allocate new storage:
> -		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 __guard(__new_start, __len, *this);
>   
>   		auto& __alloc = _M_get_Tp_allocator();
> 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
>   #define __glibcxx_want_allocator_traits_is_always_equal
>   #define __glibcxx_want_constexpr_char_traits
>   #define __glibcxx_want_constexpr_string
> diff --git a/libstdc++-v3/include/std/vector b/libstdc++-v3/include/std/vector
> index 343483e9519..ac041cc9408 100644
> --- a/libstdc++-v3/include/std/vector
> +++ b/libstdc++-v3/include/std/vector
> @@ -79,6 +79,7 @@
>   #endif
>   
>   #define __glibcxx_want_algorithm_default_value_type
> +#define __glibcxx_want_allocate_at_least
>   #define __glibcxx_want_allocator_traits_is_always_equal
>   #define __glibcxx_want_constexpr_vector
>   #define __glibcxx_want_containers_ranges
> diff --git a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
> index 5399096d294..987e2472490 100644
> --- a/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
> +++ b/libstdc++-v3/testsuite/20_util/allocator/allocate_at_least.cc
> @@ -29,7 +29,7 @@ template <typename T>
>         }
>     };
>   
> -int main()
> +void base()
>   {
>     std::allocator<X> native;
>     auto a1 = native.allocate_at_least(100);
> @@ -63,3 +63,109 @@ int main()
>     VERIFY(a5.ptr == minimal.keep);
>     minimal_traits::deallocate(minimal, a5.ptr, a5.count);
>   }
> +
> +void extra()
> +{
> +  using SatC = std::allocator_traits<std::allocator<char>>;
> +  std::allocator<char> satc;
> +  {
> +    auto [p, n] = SatC::allocate_at_least(satc, 1);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    SatC::deallocate(satc, p, n);
> +  }
> +  {
> +    auto [p, n] = SatC::allocate_at_least(satc, 2);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    SatC::deallocate(satc, p, n);
> +  }
> +  {
> +    auto [p, n] =
> +      SatC::allocate_at_least(satc, __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    SatC::deallocate(satc, p, n);
> +  }
> +  {
> +    auto [p, n] = SatC::allocate_at_least(
> +	satc, __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    SatC::deallocate(satc, p, n);
> +  }
> +
> +  using SatS = std::allocator_traits<std::allocator<short>>;
> +  std::allocator<short> sats;
> +  {
> +    auto [p, n] = SatS::allocate_at_least(sats, 1);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
> +    SatS::deallocate(sats, p, n);
> +  }
> +  {
> +    auto [p, n] = SatS::allocate_at_least(sats, 2);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
> +    SatS::deallocate(sats, p, n);
> +  }
> +  {
> +    auto [p, n] = SatS::allocate_at_least(sats,
> +	(__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1) / sizeof(short));
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
> +    SatS::deallocate(sats, p, n);
> +  }
> +  {
> +    auto [p, n] = SatS::allocate_at_least(sats,
> +	__STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(short));
> +    SatS::deallocate(sats, p, n);
> +  }
> +
> +  struct A3 { char s[3]; };
> +  using SatA3 = std::allocator_traits<std::allocator<A3>>;
> +  std::allocator<A3> sata3;
> +  {
> +    auto [p, n] = SatA3::allocate_at_least(sata3, 1);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
> +    SatA3::deallocate(sata3, p, n);
> +  }
> +  {
> +    auto [p, n] = SatA3::allocate_at_least(sata3, 2);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
> +    SatA3::deallocate(sata3, p, n);
> +  }
> +  {
> +    auto [p, n] = SatA3::allocate_at_least(sata3,
> +	(__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1) / sizeof(A3));
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
> +    SatA3::deallocate(sata3, p, n);
> +  }
> +  {
> +    auto [p, n] = SatA3::allocate_at_least(sata3,
> +	__STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A3));
> +    SatA3::deallocate(sata3, p, n);
> +  }
> +
> +  struct Anm1 { char s[__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1]; };
> +  using SatAnm1 = std::allocator_traits<std::allocator<Anm1>>;
> +  std::allocator<Anm1> satanm1;
> +  {
> +    auto [p, n] = SatAnm1::allocate_at_least(satanm1, 1);
> +    VERIFY(n == 1);
> +    SatAnm1::deallocate(satanm1, p, n);
> +  }
> +  {
> +    auto [p, n] = SatAnm1::allocate_at_least(satanm1,
> +	__STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    SatAnm1::deallocate(satanm1, p, n);
> +  }
> +  {
> +    auto [p, n] = SatAnm1::allocate_at_least(satanm1,
> +	__STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1);
> +    VERIFY(n == __STDCPP_DEFAULT_NEW_ALIGNMENT__);
> +    SatAnm1::deallocate(satanm1, p, n);
> +  }
> +}
> +
> +int main()
> +{
> +  base();
> +  extra();
> +}
> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/18654.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/18654.cc
> index d542f34d08e..c12b39196e6 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/18654.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/18654.cc
> @@ -58,7 +58,8 @@ void test01()
>   #else
>         str.shrink_to_fit(); // reserve is deprecated in C++20
>   #endif
> -      VERIFY( str.capacity() == i );
> +      unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
> +      VERIFY( str.capacity() - i <= limit);
>       }
>   }
>   
> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc
> index a26d524dddf..e307bd45bfc 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/char/shrink_to_fit.cc
> @@ -30,7 +30,8 @@ void test01()
>     s.push_back('b');
>     VERIFY( s.size() < s.capacity() );
>     s.shrink_to_fit();
> -  VERIFY( s.size() == s.capacity() );
> +  unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
> +  VERIFY( s.capacity() - s.size() <= limit );
>   }
>   
>   int main()
> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc
> index 49e45c764c4..238c56548cf 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/18654.cc
> @@ -50,6 +50,7 @@ void test01()
>         const size_type cap = str.capacity();
>         VERIFY( cap >= 3 * i );
>   
> +      // no shrink.
>         str.reserve(2 * i);
>         VERIFY( str.capacity() == cap );
>   
> @@ -58,7 +59,8 @@ void test01()
>   #else
>         str.shrink_to_fit(); // reserve is deprecated in C++20
>   #endif
> -      VERIFY( str.capacity() == i );
> +      unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t) - 1;
> +      VERIFY( str.capacity() - i <= limit);
>       }
>   }
>   
> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/2.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/2.cc
> index bff2bdd1862..433f92cd4ae 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/2.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/2.cc
> @@ -27,14 +27,17 @@
>   void test02()
>   {
>     std::wstring str01 = L"twelve chars";
> -  // str01 becomes shared
> -  std::wstring str02 = str01;
> +  str01.reserve(100);
>   #if __cplusplus <= 201703L
>     str01.reserve();
>   #else
>     str01.shrink_to_fit(); // reserve is deprecated in C++20
>   #endif
> -  VERIFY( str01.capacity() == 12 );
> +  // These are not guaranteed to absolutely minimize storage.
> +  // allocator<wchar_t>::allocate_at_least rounds up to what
> +  // it knows ::op new delivers.
> +  unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t) - 1;
> +  VERIFY( str01.capacity() - str01.size() <= limit);
>   }
>   
>   int main()
> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/shrink_to_fit.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/shrink_to_fit.cc
> index b4d0224a9d8..ed776512485 100644
> --- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/shrink_to_fit.cc
> +++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/wchar_t/shrink_to_fit.cc
> @@ -30,7 +30,8 @@ void test01()
>     s.push_back(L'b');
>     VERIFY( s.size() < s.capacity() );
>     s.shrink_to_fit();
> -  VERIFY( s.size() == s.capacity() );
> +  unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t) - 1;
> +  VERIFY( s.capacity() - s.size() <= limit );
>   }
>   
>   int main()
> diff --git a/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc b/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc
> index 74c68712b47..843d7eb01d4 100644
> --- a/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc
> +++ b/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc
> @@ -30,11 +30,8 @@ void test01()
>     v.push_back(1);
>     VERIFY( v.size() < v.capacity() );
>     v.shrink_to_fit();
> -#if __cpp_exceptions
> -  VERIFY( v.size() == v.capacity() );
> -#else
> -  VERIFY( v.size() < v.capacity() );
> -#endif
> +  unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
> +  VERIFY(v.capacity() - v.size() <= limit);
>   }
>   
>   int main()
> diff --git a/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit2.cc b/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit2.cc
> index c8faa9ded80..91458c57dd9 100644
> --- a/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit2.cc
> +++ b/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit2.cc
> @@ -32,7 +32,8 @@ void test01()
>     v.reserve(100);
>     VERIFY( v.size() < v.capacity() );
>     v.shrink_to_fit();
> -  VERIFY( v.size() == v.capacity() );
> +  unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
> +  VERIFY( v.capacity() - v.size() <= limit);
>     VERIFY( v.get_allocator().get_personality() == alloc.get_personality() );
>   }
>   
> @@ -45,7 +46,8 @@ void test02()
>     v.reserve(100);
>     VERIFY( v.size() < v.capacity() );
>     v.shrink_to_fit();
> -  VERIFY( v.size() == v.capacity() );
> +  unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
> +  VERIFY( v.capacity() - v.size() <= limit);
>     VERIFY( v.get_allocator().get_personality() == alloc.get_personality() );
>   }
>   
> diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc
> index 00a0c7b06ea..eb501a69c0e 100644
> --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc
> +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/emplace/self_emplace.cc
> @@ -99,37 +99,29 @@ struct A
>   void
>   test03()
>   {
> -  std::vector<A> va =
> -    {
> -      { A(1) },
> -      { A(2) },
> -      { A(3) }
> -    };
> -
> -  // Make sure emplace will imply reallocation.
> -  VERIFY( va.capacity() == 3 );
> +  unsigned fit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A);
> +  std::vector<A> va; va.reserve(fit);
> +  for (int i = 1; va.size() < va.capacity(); ++i)
> +      va.push_back(A(i));
>   
>     va.emplace(va.begin(), va.begin());
>   
> -  VERIFY( va.size() == 4 );
> +  VERIFY( va.size() == fit + 1 );
>     VERIFY( va[0]._i == 1 );
>   }
>   
>   void
>   test04()
>   {
> -  std::vector<A> va =
> -    {
> -      { A(1) },
> -      { A(2) },
> -      { A(3) }
> -    };
> +  unsigned fit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A);
> +  std::vector<A> va; va.reserve(fit);
> +  for (int i = 1; va.size() < va.capacity() - 1; ++i)
> +      va.push_back(A(i));
>   
>     // Make sure emplace won't reallocate.
> -  va.reserve(4);
>     va.emplace(va.begin(), va.begin());
>   
> -  VERIFY( va.size() == 4 );
> +  VERIFY( va.size() == fit );
>     VERIFY( va[0]._i == 1 );
>   }
>   
> diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/libstdc++-v3/testsuite/util/testsuite_allocator.h
> index 892a385e307..3978ff2bb75 100644
> --- a/libstdc++-v3/testsuite/util/testsuite_allocator.h
> +++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h
> @@ -176,6 +176,16 @@ namespace __gnu_test
>   	return p;
>         }
>   
> +#ifdef __glibcxx_allocate_at_least
> +      std::allocation_result<pointer, size_type>
> +      allocate_at_least(size_type n)
> +      {
> +	auto r = AllocTraits::allocate_at_least(*this, n);
> +	counter_type::allocate(r.count * sizeof(T));
> +	return r;
> +      }
> +#endif
> +
>   #if __cplusplus >= 201103L
>         template<typename U, typename... Args>
>   	void
> @@ -373,6 +383,31 @@ namespace __gnu_test
>   	return p;
>         }
>   
> +#ifdef __glibcxx_allocate_at_least
> +      constexpr auto
> +      allocate_at_least(size_type n)
> +      -> std::allocation_result<Tp*, size_t>
> +      {
> +	auto r = AllocTraits::allocate_at_least(*this, n);
> +
> +	if (std::__is_constant_evaluated())
> +	  return r;
> +
> +	try
> +	  {
> +	    get_map().insert(map_type::value_type(
> +		  reinterpret_cast<void*>(r.ptr), personality));
> +	  }
> +	catch(...)
> +	  {
> +	    AllocTraits::deallocate(*this, r.ptr, r.count);
> +	    __throw_exception_again;
> +	  }
> +
> +	return r;
> +      }
> +#endif
> +
>         _GLIBCXX14_CONSTEXPR
>         void
>         deallocate(pointer p, size_type n)



More information about the Libstdc++ mailing list