[PATCH v4] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]
Jonathan Wakely
jwakely@redhat.com
Mon May 18 22:05:35 GMT 2026
On Thu, 14 May 2026 at 12:45 -0400, Nathan Myers wrote:
>[This patch may do an extra _GLIBC_ASAN_ANNOTATE_REINIT
>in vector<>::operator=(const vector&), when it calls
>_M_replace_with. Is that OK?]
It's a pessimization, but not incorrect. I don't know how large the
cost is.
>[Oddly: On a from-scratch build of GCC 17, after applying the
>patch and prior to a clean rebuild of libstdc++ (i.e. just make),
>tests for 11,14,17 failed, while 20,23,26 passed. Shouldn't
>20 have failed too?]
I haven't confirmed it, but my guess is that this is because every
member function of std::string and std::vector is constexpr in C++20,
which means they're declared inline. That means GCC tries harder to
inline them, so doesn't use the explicit instantiations from the
library. If the testcase is inlining everything, it doesn't need the
extern symbols, so doesn't care that they weren't exported from the
shared library.
I can reproduce your observation, and if I re-run one of the tests
that fails for 11,14,17 using -O0 I expected it to fail for 20,23,26
too. In fact I see the opposite, it PASSes for 11,14,17. So now I'm
confused.
>Changes in v4:
> - Move mangled string symbols from _GLIBCXX_3.4.35 to .36, to
> place in GCC 17.
> - Correct whitespace on some #if/#else/#endifs.
> - Change 'auto ... -> result' decl style to old form on added
> and changed decls.
> - Make allocator_traits<>::allocate_at_least return the result
> of delegating directly, without destructuring and re-
> assembling; add CustomPointerAlloc::allocate_at_least to
> perform the regular-to-fancy pointer conversion instead.
> - Make allocator_traits<void>::allocate_at_least return
> allocator_result<void*, size_t>, not <pointer,...>.
> - Minimize string::_S_allocate for just ABI retention use,
> and call allocator_traits<>::allocate directly.
> - Make string::_M_replace use _S_allocate_at_least in its
> consteval part, to catch its if consteval contruct_at block.
> - Move string::_M_create_and_place defn to bits/basic_string.h,
> annotate __always_inline__.
> - Change __new_allocator<>::_M_max_size to static _S_max_size.
> - Add __new_allocator<>::_S_check_allocation_limit using new
> _S_max_size, to simplify allocate and allocate_at_least.
> - Rejigger __new_allocator<>::allocate and allocate_at_least
> so the latter delegates to allocate unless it might really be
> able to allocate extra objects, confining compications to one
> place.
> - Remove orphaned forward decl in memoryfwd.h.
What does this line refer to?
>
>Changes in v3:
> - Make allocator_traits<allocator<T>>::allocate_at_least
> call allocator<T>::allocate_at_least directly, no checking
> needed.
> - Remove __allocate_to_alignment helper from alloc_traits.h,
> folding it back into __new_allocator::allocate_at_least.
> Replace use of std::rotl with op>>, saving one instruction
> on x86.
> - Change "if (is_constant_evaluated())" to "if consteval {"
> where allowed. This required infelicitous changes to
> indentation because, in "if consteval ... else {", the
> bracket is (needlessly) required.
> - Remove second, "hint" argument passed to allocate()
> in fallback from allocator_traits<>::allocate_at_least,
> as support for that has been deleted from the Standard.
> - Remove attempt at pass-through from allocator_traits<
> pmr::polymorphic_allocator>::allocate_at_least because we
> know it does not provide that, and just call allocate(n).
> - Retain minimalist vector<>::_M_allocate, used mainly
> to get storage for vector<bool>.
> - Rename new vector helper members
> _M_displace_storage -> _M_replace_storage, and
> _M_allocate_and_migrate -> _M_replace_with.
> - Remove __glibcxx_want_allocate_at_least from <vector>
> and <string> headers.
> - Whitespace fixups.
>
>Changes in v2:
> - Avoid PR108377 "exceeds maximum object size" warning
> differently, contained to __allocate_to_alignment.
> - Rename _S_allocate_to_alignment to __allocate_to_alignment
> because it is not a member. Generalize it for arbitrary pointer
> and size types.
> - Declare allocator_traits<allocator<void>>::allocate_at_least
> with " = delete;", matching others.
> - In allocator_traits<>::allocate_at_least, de-structure and
> reconstruct the allocation result to enable an implicit
> pointer type conversion on the way, as used (e.g.) for
> testsuite_allocator.h.
> - Define allocate_at_least in the polymorphic specialization
> of allocator_traits (bits/memory_resource.h), and in the
> testsuite allocator, destructuring likewise.
> - In string:
> - Make _M_create use _M_allocate_at_least. (This function
> is no longer used, but is retained for ABI stability.)
> - Define (in addition to _M_create_plus) a new allocation
> helper function _M_create_and_place to abstract common
> operations, and use it where suitable.
> - Add new member signatures to config/abi/pre/gnu.ver.
> - Relax shrink_to_fit()/reserve() to allow a little extra.
> - In vector, define (on top of _S_allocate_at_least) helper
> functions _M_allocate_and_copy, _M_displace_storage,
> and _M_allocate_and_migrate to abstract common ops, and
> use them. This incidentally corrects an omission of a
> _GLIBCXX_ASAN_ANNOTATE_REINIT.
> - Adjust tests to pass with or without allocate_at_least.
> (__STDCPP_DEFAULT_NEW_ALIGNMENT__ is not defined everywhere.)
>
>Changes from RFC:
> - Improve doxygen for new interfaces.
> - Move code implementing alignment size-round-up logic from
> allocator.h to alloc_traits.h for other allocators' reuse.
> - Restore allocator.h definition of allocate_at_least, that
> then explicitly delegates to its base class implementation.
> - Retain existing string::_S_allocate, vector<>::_M_allocate
> interfaces for ABI stability, using new names
> _S_allocate_at_least, _M_allocate_at_least for new behavior.
> - Define string _M_create_plus using S_allocate_at_least, and
> use that throughout in place of _M_create, retaining _M_create
> for ABI stability.
> - Make memory_resource::allocate_at_least use argument
> allocator's allocate_at_least if present.
> - Fix std::allocator<>::allocate_at_least so it takes its
> allocator object by reference, not by value.
> - Per review, in constexpr context begin lifetime of all
> potential string characters upon allocation.
> - Revert "< 2011"-only vector<>::_M_initialize_dispatch changes.
> - Fix string new-capacity off-by-one errors.
> - Export new symbols _S_allocate_at_least and _M_create_plus from
> bits/basic_string.h.
> - Work around PR108377 spurious "exceeds maximum object size".
> - Test.
> - Patch too-strict capacity tests.
>
>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.
Is the "Provide apparatus for ..." sentence still true? I think that
was dropped in an earlier revision.
>
>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
> * config/abi/pre/gnu.ver: Expose string::_S_allocate_at_least,
> _M_create_plus symbols.
> * include/bits/alloc_traits.h:
> (allocate_at_least): Delegate in allocator_traits<allocator<_Tp>>
> specialization to allocator<_Tp>::allocate_at_least, unconditionally;
> annotate [[__gnu__::always_inline__]].
> (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, using it.
> (_S_allocate): Minimize for legacy ABI use only.
> (_M_create_plus): Declare.
> (_M_create_and_place): Define, abstracting common operations.
> (assign): Use _S_allocate_at_least.
> * include/bits/basic_string.tcc:
> (_M_create_plus): Define.
> (_M_replace, reserve): Use _S_allocate_at_least.
> (_M_construct, others (3x)): Use _M_create_and_place.
> (_M_construct, input iterators): Use _M_create_plus.
> (_M_create, _M_assign, reserve, _M_mutate): Same.
> * include/bits/memory_resource.h (allocate_at_least): Define,
> document.
> * include/bits/new_allocator.h (allocate_at_least): Define.
Please mention that 'allocate' was changed to use
_S_check_allocation_size.
OK with those two changes to the commit log, thanks!
> (_S_check_allocation_size) Define.
> (_S_max_size): Change from _M_max_size.
> (deallocate): Refine "if constexpr" logic.
> * include/bits/stl_vector.h:
> (_S_max_size): Move to _Vector_base.
> (_Alloc_result): Define type.
> (_M_allocate_at_least): Define, using allocate_at_least where supported.
> (_M_allocate): Delegate to _M_allocate_at_least.
> (max_size, _S_check_init_len): Use _S_max_size as moved.
> (_M_create_storage, append_range, _M_allocate_and_copy,
> _M_replace_storage): Define, abstracting common operations.
> (_M_replace_with): Define, likewise.
> (_M_range_initialize_n): Use _M_allocate_at_least.
> (_M_check_len): Improve logic.
> * include/bits/vector.tcc:
> (reserve, _M_fill_append, _M_range_insert): Use _M_allocate_at_least
> and _M_replace_storage.
> (operator=, _M_assign_aux): Use _M_replace_with.
> (_M_realloc_insert, _M_realloc_append, _M_default_append, insert_range):
> Use _M_allocate_at_least.
> (_M_fill_insert): Use _M_replace_storage, normalize whitespace.
> * testsuite/util/testsuite_allocator.h:
> (allocate_at_least (3x)): 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.
>---
> libstdc++-v3/config/abi/pre/gnu.ver | 4 +
> libstdc++-v3/include/bits/alloc_traits.h | 18 +-
> libstdc++-v3/include/bits/allocator.h | 30 +++-
> libstdc++-v3/include/bits/basic_string.h | 44 ++++-
> libstdc++-v3/include/bits/basic_string.tcc | 96 ++++++-----
> libstdc++-v3/include/bits/memory_resource.h | 16 ++
> libstdc++-v3/include/bits/new_allocator.h | 77 ++++++---
> libstdc++-v3/include/bits/stl_vector.h | 136 +++++++++++----
> libstdc++-v3/include/bits/vector.tcc | 155 +++++++-----------
> .../20_util/allocator/allocate_at_least.cc | 108 +++++++++++-
> .../basic_string/capacity/char/18654.cc | 7 +-
> .../capacity/char/shrink_to_fit.cc | 7 +-
> .../basic_string/capacity/wchar_t/18654.cc | 8 +-
> .../basic_string/capacity/wchar_t/2.cc | 13 +-
> .../capacity/wchar_t/shrink_to_fit.cc | 7 +-
> .../vector/capacity/shrink_to_fit.cc | 7 +-
> .../vector/capacity/shrink_to_fit2.cc | 14 +-
> .../vector/modifiers/emplace/self_emplace.cc | 36 ++--
> .../testsuite/util/testsuite_allocator.h | 43 +++++
> 19 files changed, 585 insertions(+), 241 deletions(-)
>
>diff --git a/libstdc++-v3/config/abi/pre/gnu.ver b/libstdc++-v3/config/abi/pre/gnu.ver
>index 06892cd2d18..bf4d7338cb6 100644
>--- a/libstdc++-v3/config/abi/pre/gnu.ver
>+++ b/libstdc++-v3/config/abi/pre/gnu.ver
>@@ -2628,6 +2628,10 @@ GLIBCXX_3.4.36 {
>
> _ZNSt6chrono8__detail25__recent_leap_second_infoERNS_16leap_second_infoEj;
>
>+ # basic_string::allocate_at_least
>+ _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_S_allocate_*;
>+ _ZNSt7__cxx1112basic_stringI[cw]St11char_traitsI[cw]ESaI[cw]EE*_M_create_*;
>+
> } GLIBCXX_3.4.35;
>
> # Symbols in the support library (libsupc++) have their own tag.
>diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
>index e4e0f1608de..101badff454 100644
>--- a/libstdc++-v3/include/bits/alloc_traits.h
>+++ b/libstdc++-v3/include/bits/alloc_traits.h
>@@ -417,9 +417,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * is obliged to reserve more space than required for the cited
> * `n` objects, it may deliver the extra space to the caller.
> */
>- [[nodiscard]] static constexpr auto
>+ [[nodiscard]] static constexpr allocation_result<pointer, size_type>
> allocate_at_least(_Alloc& __a, size_type __n)
>- -> allocation_result<pointer, size_type>
> {
> if constexpr (requires { __a.allocate_at_least(__n); })
> return __a.allocate_at_least(__n);
>@@ -664,14 +663,14 @@ _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)`.
> */
>- [[nodiscard]] static constexpr auto
>+ [[nodiscard,__gnu__::__always_inline__]]
>+ static constexpr allocation_result<pointer, size_type>
> allocate_at_least(allocator_type& __a, size_type __n)
>- -> allocation_result<pointer, size_type>
> { return __a.allocate_at_least(__n); }
> #endif
>
>@@ -822,6 +821,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> static void*
> allocate(allocator_type&, size_type, const void* = nullptr) = delete;
>
>+#ifdef __glibcxx_allocate_at_least
>+ static allocation_result<void*, 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;
>@@ -872,7 +876,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> select_on_container_copy_construction(const allocator_type& __rhs)
> { return __rhs; }
> };
>-#endif
>+#endif // _GLIBCXX_HOSTED
>
> /// @cond undocumented
> #pragma GCC diagnostic push
>diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/include/bits/allocator.h
>index 9c22c805ebe..9c9ebdeb796 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,27 @@ _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 consteval
>+ { 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), __n };
>+ }
>+ }
>+#endif
>
>+#if __cpp_constexpr_dynamic_alloc // >= C++20
> [[__gnu__::__always_inline__]]
> constexpr void
> deallocate(_Tp* __p, size_t __n)
>@@ -219,12 +239,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..c1871633f1b 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.
> static _GLIBCXX20_CONSTEXPR pointer
> _S_allocate(_Char_alloc_type& __a, size_type __n)
>+ { return _Alloc_traits::allocate(__a, __n); }
>+
>+ 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,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
>
> // Create & Destroy
> _GLIBCXX20_CONSTEXPR
>+ _Alloc_result
>+ _M_create_plus(size_type __new_capacity, size_type __old_capacity);
>+
>+ __attribute__((__always_inline__))
>+ _GLIBCXX20_CONSTEXPR
>+ void
>+ _M_create_and_place(size_type __new_capacity, size_type __old_capacity)
>+ {
>+ _Alloc_result __r = _M_create_plus(__new_capacity, __old_capacity);
>+ _M_data(__r.__ptr);
>+ _M_capacity(__r.__count - 1); // Leave room for NUL.
>+ }
>+
>+ // This must remain for ABI stability though unused.
>+ _GLIBCXX20_CONSTEXPR
> pointer
> _M_create(size_type&, size_type);
>
>@@ -1782,10 +1812,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..5f83991db83 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,19 @@ _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);
>+ }
>+
>+ // 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);
>+ __capacity = __r.__count - 1; // Leave room for NUL.
>+ return __r.__ptr;
> }
>
> // NB: This is the special case for Input Iterators, used in
>@@ -203,11 +215,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 +243,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 +273,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 +287,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 +350,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 +378,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 +395,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 +433,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 +449,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 +597,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);
>@@ -677,7 +687,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 +926,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..44c63a03354 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,21 @@ 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(n), n }`.
>+ */
>+ [[nodiscard]] static std::allocation_result<pointer, size_type>
>+ allocate_at_least(allocator_type& __a, size_type __n)
>+ { 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..4524355a4a0 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)
> {
>@@ -123,6 +124,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #pragma GCC diagnostic push
> #pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
>
>+ __attribute__((__always_inline__))
>+ _GLIBCXX20_CONSTEXPR static void
>+ _S_check_allocation_limit(size_t __n)
>+ {
>+ if (__builtin_expect(__n > _S_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();
>+ }
>+ }
>+
> // NB: __n is permitted to be 0. The C++ standard says nothing
> // about what the return value is when __n == 0.
> _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR _Tp*
>@@ -142,25 +157,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> else
> #endif
> #endif
>- 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();
>- }
>+ _S_check_allocation_limit(__n);
> #if __cpp_aligned_new && __cplusplus >= 201103L
>- else if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>+ if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>+ {
>+ std::align_val_t __al = std::align_val_t(alignof(_Tp));
>+ return static_cast<_Tp*>(
>+ _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp), __al));
>+ }
>+ else
>+#endif
>+ return static_cast<_Tp*>(
>+ _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
>+ }
>+ }
>+
>+#ifdef __glibcxx_allocate_at_least // C++23
>+ [[nodiscard]] constexpr std::allocation_result<_Tp*, size_t>
>+ allocate_at_least(size_t __n)
>+ {
>+ if ! consteval
> {
>- std::align_val_t __al = std::align_val_t(alignof(_Tp));
>- return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp),
>- __al));
>+ if constexpr (requires { sizeof(_Tp); })
>+ if constexpr (alignof(_Tp) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>+ if constexpr ( sizeof(_Tp) < __STDCPP_DEFAULT_NEW_ALIGNMENT__)
>+ {
>+ _S_check_allocation_limit(__n);
>+ const size_t __need = __n * sizeof(_Tp);
>+ const size_t __mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>+ size_t __ask = (__need + __mask) & ~__mask;
>+ // Avoid rounding up to and asking for 2^63 bytes (PR108377):
>+ __ask -= __ask >> (__SIZE_WIDTH__ - 1);
>+ auto* __p = static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__ask));
>+ using _U8 = const unsigned char;
>+ static_assert(sizeof(_Tp) <= ~_U8());
>+ // Use 8-bit division for minimal latency:
>+ _U8 __spare = __ask - __need, __size = sizeof(_Tp);
>+ return { __p , __n + __spare / __size };
>+ }
> }
>-#endif
>- else
>- return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));
>+ return { allocate(__n), __n };
> }
>+#endif
>
> // __p is not permitted to be a null pointer.
> _GLIBCXX20_CONSTEXPR void
>@@ -177,10 +216,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
>@@ -192,7 +231,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> __attribute__((__always_inline__))
> size_type
> max_size() const _GLIBCXX_USE_NOEXCEPT
>- { return _M_max_size(); }
>+ { return _S_max_size(); }
>
> #if __cplusplus >= 201103L
> template<typename _Up, typename... _Args>
>@@ -238,8 +277,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> private:
> __attribute__((__always_inline__))
>- _GLIBCXX_CONSTEXPR size_type
>- _M_max_size() const _GLIBCXX_USE_NOEXCEPT
>+ _GLIBCXX_CONSTEXPR static size_type
>+ _S_max_size() _GLIBCXX_USE_NOEXCEPT
> {
> #if __PTRDIFF_MAX__ < __SIZE_MAX__
> return std::size_t(__PTRDIFF_MAX__) / sizeof(_Tp);
>diff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h
>index c4ca214752a..0c9b74fdbb2 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
>@@ -389,6 +402,43 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
> }
>
>+ 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;
>+ _Alloc_result __r;
>+ if (__builtin_expect(__n != 0, true))
>+ {
>+#ifdef __glibcxx_allocate_at_least // C++23
>+ if constexpr (requires { _Tr::allocate_at_least(_M_impl, __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, __count };
>+ }
>+ else
>+#endif
>+ {
>+ __r.__ptr = _Tr::allocate(_M_impl, __n);
>+ __r.__count = __n;
>+ }
>+ }
>+ else
>+ {
>+ __r.__ptr = pointer();
>+ __r.__count = 0;
>+ }
>+ return __r;
>+ }
>+
> _GLIBCXX20_CONSTEXPR
> void
> _M_deallocate(pointer __p, size_t __n)
>@@ -404,9 +454,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 +530,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 +587,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 +1168,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 +1734,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 +1786,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 +1942,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
>+ _Alloc_result
> _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();
>+ (void) __guard._M_release();
>+ return __r;
> }
>
>+ _GLIBCXX20_CONSTEXPR void
>+ _M_replace_storage(pointer __start, pointer __end, size_type __cap)
>+ {
>+ _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_replace_with(size_type __n,
>+ _ForwardIterator __first, _ForwardIterator __last)
>+ {
>+ _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_replace_storage(__r.__ptr, __r.__ptr + __n, __r.__count);
>+ }
>
> // Internal constructor functions follow.
>
>@@ -1916,6 +1994,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
> _M_initialize_dispatch(_Integer __int_n, _Integer __value, __true_type)
> {
> const size_type __n = static_cast<size_type>(__int_n);
>+ // NB this is c++98 code, so has no allocate_at_least.
> pointer __start =
> _M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator()));
> this->_M_impl._M_start = __start;
>@@ -1971,10 +2050,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 +2271,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..86c41d74ff4 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_replace_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_replace_with(__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_replace_with(__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_replace_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_replace_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_replace_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/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..59012a1c170 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,12 @@ void test01()
> #else
> str.shrink_to_fit(); // reserve is deprecated in C++20
> #endif
>- VERIFY( str.capacity() == i );
>+#if __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>+#else
>+ unsigned limit = 0;
>+#endif
>+ 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..ae5df746f35 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,12 @@ void test01()
> s.push_back('b');
> VERIFY( s.size() < s.capacity() );
> s.shrink_to_fit();
>- VERIFY( s.size() == s.capacity() );
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;
>+#else
>+ unsigned limit = 0;
>+#endif
>+ 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..bf3b8dd5581 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,12 @@ void test01()
> #else
> str.shrink_to_fit(); // reserve is deprecated in C++20
> #endif
>- VERIFY( str.capacity() == i );
>+#if __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t) - 1;
>+#else
>+ unsigned limit = 0;
>+#endif
>+ 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..04a3763fc77 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,21 @@
> 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.
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t) - 1;
>+#else
>+ unsigned limit = 0;
>+#endif
>+ 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..fc0f659539f 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,12 @@ void test01()
> s.push_back(L'b');
> VERIFY( s.size() < s.capacity() );
> s.shrink_to_fit();
>- VERIFY( s.size() == s.capacity() );
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(wchar_t) - 1;
>+#else
>+ unsigned limit = 0;
>+#endif
>+ 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..d4aa4d89b56 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,12 @@ void test01()
> v.push_back(1);
> VERIFY( v.size() < v.capacity() );
> v.shrink_to_fit();
>-#if __cpp_exceptions
>- VERIFY( v.size() == v.capacity() );
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
> #else
>- VERIFY( v.size() < v.capacity() );
>+ unsigned limit = 0;
> #endif
>+ 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..9489e043845 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,12 @@ void test01()
> v.reserve(100);
> VERIFY( v.size() < v.capacity() );
> v.shrink_to_fit();
>- VERIFY( v.size() == v.capacity() );
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
>+#else
>+ unsigned limit = 0;
>+#endif
>+ VERIFY( v.capacity() - v.size() <= limit);
> VERIFY( v.get_allocator().get_personality() == alloc.get_personality() );
> }
>
>@@ -45,7 +50,12 @@ void test02()
> v.reserve(100);
> VERIFY( v.size() < v.capacity() );
> v.shrink_to_fit();
>- VERIFY( v.size() == v.capacity() );
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
>+#else
>+ unsigned limit = 0;
>+#endif
>+ 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..629f35f05ba 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,37 @@ struct A
> void
> test03()
> {
>- std::vector<A> va =
>- {
>- { A(1) },
>- { A(2) },
>- { A(3) }
>- };
>-
>- // Make sure emplace will imply reallocation.
>- VERIFY( va.capacity() == 3 );
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned fit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A);
>+#else
>+ unsigned fit = 4;
>+#endif
>+ 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) }
>- };
>+#ifdef __glibcxx_allocate_at_least
>+ unsigned fit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(A);
>+#else
>+ unsigned fit = 4;
>+#endif
>+ 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..27372a97161 100644
>--- a/libstdc++-v3/testsuite/util/testsuite_allocator.h
>+++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h
>@@ -168,6 +168,16 @@ namespace __gnu_test
> : Alloc(alloc)
> { }
>
>+#ifdef __glibcxx_allocate_at_least // C++23
>+ std::allocation_result<pointer, size_type>
>+ allocate_at_least(size_type n)
>+ {
>+ auto [p, c] = AllocTraits::allocate_at_least(*this, n);
>+ counter_type::allocate(c * sizeof(T));
>+ return { p, c };
>+ }
>+#endif
>+
> pointer
> allocate(size_type n, const void* = 0)
> {
>@@ -373,6 +383,32 @@ 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 consteval
>+ { return r; }
>+ else
>+ {
>+ 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)
>@@ -632,6 +668,13 @@ namespace __gnu_test
> pointer allocate(std::size_t n, const_void_pointer = {})
> { return pointer(std::allocator<Tp>::allocate(n)); }
>
>+#ifdef __glibcxx_allocate_at_least
>+ _GLIBCXX14_CONSTEXPR
>+ std::allocation_result<pointer, std::size_t>
>+ allocate_at_least(std::size_t n)
>+ { return { allocate(n), n }; }
>+#endif
>+
> _GLIBCXX14_CONSTEXPR
> void deallocate(pointer p, std::size_t n)
> { std::allocator<Tp>::deallocate(std::addressof(*p), n); }
>--
>2.53.0
>
>
More information about the Libstdc++
mailing list