Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2219744/?format=api
{ "id": 2219744, "url": "http://patchwork.ozlabs.org/api/patches/2219744/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260404050329.949729-1-ncm@cantrip.org/", "project": { "id": 17, "url": "http://patchwork.ozlabs.org/api/projects/17/?format=api", "name": "GNU Compiler Collection", "link_name": "gcc", "list_id": "gcc-patches.gcc.gnu.org", "list_email": "gcc-patches@gcc.gnu.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260404050329.949729-1-ncm@cantrip.org>", "list_archive_url": null, "date": "2026-04-04T04:52:35", "name": "[RFC] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "3fedbc15e479123804e7a541bb47e36e8a535bbb", "submitter": { "id": 90892, "url": "http://patchwork.ozlabs.org/api/people/90892/?format=api", "name": "Nathan Myers", "email": "ncm@cantrip.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260404050329.949729-1-ncm@cantrip.org/mbox/", "series": [ { "id": 498697, "url": "http://patchwork.ozlabs.org/api/series/498697/?format=api", "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=498697", "date": "2026-04-04T04:52:35", "name": "[RFC] libstdc++: Use allocate_at_least in vector, string (P0401) [PR118030]", "version": 1, "mbox": "http://patchwork.ozlabs.org/series/498697/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2219744/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2219744/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Delivered-To": [ "patchwork-incoming@legolas.ozlabs.org", "gcc-patches@gcc.gnu.org" ], "Authentication-Results": [ "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)", "sourceware.org;\n dmarc=none (p=none dis=none) header.from=cantrip.org", "sourceware.org; spf=fail smtp.mailfrom=cantrip.org", "server2.sourceware.org;\n arc=none smtp.remote-ip=205.139.111.44" ], "Received": [ "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fnk666c3Fz1yCs\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 04 Apr 2026 16:04:27 +1100 (AEDT)", "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id AFA774BA903D\n\tfor <incoming@patchwork.ozlabs.org>; Sat, 4 Apr 2026 05:04:15 +0000 (GMT)", "from us-smtp-delivery-44.mimecast.com\n (us-smtp-delivery-44.mimecast.com [205.139.111.44])\n by sourceware.org (Postfix) with ESMTP id 83F1B4BA23E3\n for <gcc-patches@gcc.gnu.org>; Sat, 4 Apr 2026 05:03:37 +0000 (GMT)", "from mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-35-165-154-97.us-west-2.compute.amazonaws.com [35.165.154.97]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-170-T_ygWAgnOaGS9fx_6aeWmQ-1; Sat,\n 04 Apr 2026 01:03:35 -0400", "from mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.12])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-06.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 1E6141800281; Sat, 4 Apr 2026 05:03:34 +0000 (UTC)", "from redhat.redhat.com (unknown [10.44.34.194])\n by mx-prod-int-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id 545EC19560A6; Sat, 4 Apr 2026 05:03:30 +0000 (UTC)" ], "DKIM-Filter": [ "OpenDKIM Filter v2.11.0 sourceware.org AFA774BA903D", "OpenDKIM Filter v2.11.0 sourceware.org 83F1B4BA23E3" ], "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 83F1B4BA23E3", "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 83F1B4BA23E3", "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1775279017; cv=none;\n b=mXHtImf5exZyvE1X5bFPhYpX8kPAOo6yq3L2TTIalPzBZfw0TfY4308NCyoaQc984KP1Jk+mw4jGm8NA5hcMf+o9tTiyvrtSW/pxcoldGJSLV3r0eG1hmLQzmtgFLLRuaS8mJ23p3BMIhGZniPVobVCQo46wpbmMKlolaNYVfVg=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1775279017; c=relaxed/simple;\n bh=b6WFAPOqWqEVZQGp8vWdGrwFQT1wlAHVUuU1QkP7KNY=;\n h=From:To:Subject:Date:Message-ID:MIME-Version;\n b=FQG3diDmpirpngBq9jGAT9va49PjMbXFgsVZ39ESEuoOVkF+XV7JgG7Ewo8YY7IxEyCYC+MVpI/jfSMuqapxAbjpn3AM931Z89GO1bKB0qa0vVlitg3ck+RPtl2oFzB7bAsOcbhylvG07wuvkjlRpYy6ilU+4Wmd4dSLIArd0o4=", "ARC-Authentication-Results": "i=1; server2.sourceware.org", "X-MC-Unique": "T_ygWAgnOaGS9fx_6aeWmQ-1", "X-Mimecast-MFC-AGG-ID": "T_ygWAgnOaGS9fx_6aeWmQ_1775279014", "From": "Nathan Myers <ncm@cantrip.org>", "To": "gcc-patches@gcc.gnu.org,\n\tlibstdc++@gcc.gnu.org", "Subject": "[RFC] libstdc++: Use allocate_at_least in vector,\n string (P0401) [PR118030]", "Date": "Sat, 4 Apr 2026 00:52:35 -0400", "Message-ID": "<20260404050329.949729-1-ncm@cantrip.org>", "MIME-Version": "1.0", "X-Scanned-By": "MIMEDefang 3.0 on 10.30.177.12", "X-Mimecast-Spam-Score": "0", "X-Mimecast-MFC-PROC-ID": "3G9H8HA3fvyqQyxDQqgu8k1q4_oT1R7STKLdtAZuV0g_1775279014", "X-Mimecast-Originator": "cantrip.org", "Content-Transfer-Encoding": "quoted-printable", "content-type": "text/plain; charset=WINDOWS-1252; x-default=true", "X-BeenThere": "gcc-patches@gcc.gnu.org", "X-Mailman-Version": "2.1.30", "Precedence": "list", "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>", "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>", "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>", "List-Post": "<mailto:gcc-patches@gcc.gnu.org>", "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>", "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>", "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org" }, "content": "- Implement as much of allocator<>::allocate_at_least as is\n possible relying solely on known alignment behavior of\n standard operator new.\n - Use allocator_at_least in string and vector to maximize usage\n of actually allocated storage, so far as is known.\n\nDefines and uses {string,vector}::_Alloc_result for all c++-*,\nto reduce churn, actually calling allocate_at_least only where\ndefined. User-defined allocators can implement larger benefits,\nand string and vector will take full advantage.\n\nTo do: Add tests; Fix constexpr test failures; By detecting\nwhether ::operator new has been replaced, we may rely on details \nof our allocator implementation.\n\nlibstdc++-v3/ChangeLog:\n\tPR libstdc++/118030\n\t* include/bits/alloc_traits.h (allocate_at_least): Call\n\t__a.allocate_at_least only if it exists.\n\t* include/bits/allocator.h (allocate_at_least): Remove.\n\t* include/bits/basic_string.h (_Alloc_result): Define.\n\t(_S_allocate): Delete.\n\t(_S_allocate_at_least): Define.\n\t(assign): Use _S_allocate_at_least.\n\t* include/bits/basic_string.tcc (_M_create, reserve, _M_replace):\n\tUse _S_allocate_at_least.\n\t* include/bits/memory_resource.h (allocate_at_least): Define.\n\t* include/bits/new_allocator.h (allocate_at_least): Define.\n\t* include/bits/stl_vector.h (_S_max_size): Move to _Vector_base.\n\t(_M_allocate): Remove.\n\t(_M_allocate_at_least): Define.\n\t(_Alloc_result): Define.\n\t(max_size, _S_check_init_len): Use _S_max_size as moved.\n\t(_M_create_storage, append_range, _M_allocate_and_copy,\n\t_M_initialize_dispatch, _M_range_initialize): Use _M_allocate_at_least.\n\t(_M_check_len): Improve logic.\n\t* include/bits/vector.tcc (reserve, _M_realloc_insert,\n\t_M_realloc_append, _M_fill_insert, _M_fill_append, _M_default_append,\n\t_M_range_insert, insert_range): Use _M_allocate_at_least.\n\t* include/std/string: Define __glibcxx_want_allocate_at_least.\n\t* include/std/vector: Same.\n\t* testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc:\n\t* testsuite/util/testsuite_allocator.h (allocate_at_least (2x)): Define.\n\t(allocate): Use allocate_at_least.\n---\n libstdc++-v3/include/bits/alloc_traits.h | 9 +-\n libstdc++-v3/include/bits/allocator.h | 6 --\n libstdc++-v3/include/bits/basic_string.h | 24 +++--\n libstdc++-v3/include/bits/basic_string.tcc | 17 ++--\n libstdc++-v3/include/bits/memory_resource.h | 16 ++++\n libstdc++-v3/include/bits/new_allocator.h | 43 +++++++++\n libstdc++-v3/include/bits/stl_vector.h | 95 ++++++++++++-------\n libstdc++-v3/include/bits/vector.tcc | 66 ++++++++-----\n libstdc++-v3/include/std/string | 1 +\n libstdc++-v3/include/std/vector | 1 +\n .../basic_string/cons/wchar_t/constexpr.cc | 2 +-\n .../testsuite/util/testsuite_allocator.h | 40 ++++++++\n 12 files changed, 239 insertions(+), 81 deletions(-)", "diff": "diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h\nindex 2be8ed561d4..b773be365bc 100644\n--- a/libstdc++-v3/include/bits/alloc_traits.h\n+++ b/libstdc++-v3/include/bits/alloc_traits.h\n@@ -419,7 +419,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n */\n [[nodiscard]] static constexpr auto\n allocate_at_least(_Alloc& __a, size_type __n)\n-\t-> allocation_result<pointer, size_type>\n+ -> allocation_result<pointer, size_type>\n {\n \tif constexpr (requires { __a.allocate_at_least(__n); })\n \t return __a.allocate_at_least(__n);\n@@ -672,7 +672,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n [[nodiscard]] static constexpr auto\n allocate_at_least(allocator_type __a, size_type __n)\n \t-> allocation_result<pointer, size_type>\n- { return __a.allocate_at_least(__n); }\n+ {\n+\tif constexpr(requires { __a.__allocate_at_least(__n); })\n+\t return __a.allocate_at_least(__n);\n+\telse\n+\t return { __a.allocate(__n), __n };\n+ }\n #endif\n \n /**\ndiff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/include/bits/allocator.h\nindex 9c22c805ebe..ec160f44a1e 100644\n--- a/libstdc++-v3/include/bits/allocator.h\n+++ b/libstdc++-v3/include/bits/allocator.h\n@@ -219,12 +219,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n }\n #endif // C++20\n \n-#ifdef __glibcxx_allocate_at_least // C++23\n- [[nodiscard]] constexpr allocation_result<_Tp*, size_t>\n- allocate_at_least(size_t __n)\n- { return { this->allocate(__n), __n }; }\n-#endif\n-\n friend __attribute__((__always_inline__)) _GLIBCXX20_CONSTEXPR\n bool\n operator==(const allocator&, const allocator&) _GLIBCXX_NOTHROW\ndiff --git a/libstdc++-v3/include/bits/basic_string.h b/libstdc++-v3/include/bits/basic_string.h\nindex 202a911f9ef..09a87d27910 100644\n--- a/libstdc++-v3/include/bits/basic_string.h\n+++ b/libstdc++-v3/include/bits/basic_string.h\n@@ -135,10 +135,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11\n #endif\n \n private:\n- static _GLIBCXX20_CONSTEXPR pointer\n- _S_allocate(_Char_alloc_type& __a, size_type __n)\n+ struct _Alloc_result { pointer __ptr; size_type __count; };\n+\n+ static _GLIBCXX20_CONSTEXPR _Alloc_result\n+ _S_allocate_at_least(_Char_alloc_type& __a, size_type __n)\n {\n-\tpointer __p = _Alloc_traits::allocate(__a, __n);\n+\t_Alloc_result __r;\n+#ifdef __glibcxx_allocate_at_least // C++23\n+\tauto [__ptr, __count] = _Alloc_traits::allocate_at_least(__a, __n);\n+\t__r.__ptr = __ptr, __r.__count = __count;\n+#else\n+\t__r.__ptr = _Alloc_traits::allocate(__a, __n), __r.__count = __n;\n+#endif\n #if __glibcxx_constexpr_string >= 201907L\n \t// std::char_traits begins the lifetime of characters,\n \t// but custom traits might not, so do it here.\n@@ -146,9 +154,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11\n \t if (std::__is_constant_evaluated())\n \t // Begin the lifetime of characters in allocated storage.\n \t for (size_type __i = 0; __i < __n; ++__i)\n-\t std::construct_at(__builtin_addressof(__p[__i]));\n+\t std::construct_at(__builtin_addressof(__r.__ptr[__i]));\n #endif\n-\treturn __p;\n+\treturn __r;\n }\n \n #ifdef __glibcxx_string_view // >= C++17\n@@ -1782,10 +1790,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11\n \t\t const auto __len = __str.size();\n \t\t auto __alloc = __str._M_get_allocator();\n \t\t // If this allocation throws there are no effects:\n-\t\t auto __ptr = _S_allocate(__alloc, __len + 1);\n+\t\t auto __r = _S_allocate_at_least(__alloc, __len + 1);\n \t\t _M_destroy(_M_allocated_capacity);\n-\t\t _M_data(__ptr);\n-\t\t _M_capacity(__len);\n+\t\t _M_data(__r.__ptr);\n+\t\t _M_capacity(__r.__count);\n \t\t _M_set_length(__len);\n \t\t }\n \t }\ndiff --git a/libstdc++-v3/include/bits/basic_string.tcc b/libstdc++-v3/include/bits/basic_string.tcc\nindex a223edf67ac..c56b214a3fd 100644\n--- a/libstdc++-v3/include/bits/basic_string.tcc\n+++ b/libstdc++-v3/include/bits/basic_string.tcc\n@@ -161,7 +161,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n \n // NB: Need an array of char_type[__capacity], plus a terminating\n // null char_type() element.\n- return _S_allocate(_M_get_allocator(), __capacity + 1);\n+ _Alloc_result __r = _S_allocate_at_least(\n+\t_M_get_allocator(), __capacity + 1);\n+ __capacity = __r.__count - 1;\n+ return __r.__ptr;\n }\n \n // NB: This is the special case for Input Iterators, used in\n@@ -444,11 +447,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n else if (__length < __capacity)\n \ttry\n \t {\n-\t pointer __tmp = _S_allocate(_M_get_allocator(), __length + 1);\n-\t this->_S_copy(__tmp, _M_data(), __length + 1);\n+\t _Alloc_result __r = _S_allocate_at_least(\n+\t _M_get_allocator(), __length + 1);\n+\t this->_S_copy(__r.__ptr, _M_data(), __length + 1);\n \t _M_dispose();\n-\t _M_data(__tmp);\n-\t _M_capacity(__length);\n+\t _M_data(__r.__ptr);\n+\t _M_capacity(__r.__count - 1); // reserve room for NUL.\n \t }\n \tcatch (const __cxxabiv1::__forced_unwind&)\n \t { throw; }\n@@ -588,7 +592,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n #if __cpp_lib_is_constant_evaluated\n \t if (std::is_constant_evaluated())\n \t {\n-\t auto __newp = _S_allocate(_M_get_allocator(), __new_size);\n+\t auto __newp =\n+\t\t_S_allocate_at_least(_M_get_allocator(), __new_size).__ptr;\n \t _S_copy(__newp, this->_M_data(), __pos);\n \t _S_copy(__newp + __pos, __s, __len2);\n \t _S_copy(__newp + __pos + __len2, __p + __len1, __how_much);\ndiff --git a/libstdc++-v3/include/bits/memory_resource.h b/libstdc++-v3/include/bits/memory_resource.h\nindex e5c6697b07e..ee3872e2bcd 100644\n--- a/libstdc++-v3/include/bits/memory_resource.h\n+++ b/libstdc++-v3/include/bits/memory_resource.h\n@@ -468,6 +468,22 @@ namespace pmr\n allocate(allocator_type& __a, size_type __n, const_void_pointer)\n { return __a.allocate(__n); }\n \n+#ifdef __glibcxx_allocate_at_least\n+ /**\n+ * @brief Allocate memory, generously.\n+ * @param __a An allocator.\n+ * @param __n The number of objects to allocate space for.\n+ * @return Memory of suitable size and alignment for `n` objects\n+ * of type `value_type`.\n+ *\n+ * Returns `a.allocate(n)`.\n+ */\n+ [[nodiscard]] static auto\n+ allocate_at_least(allocator_type& __a, size_type __n)\n+\t-> std::allocation_result<pointer, size_type>\n+ { return { __a.allocate(__n), __n }; }\n+#endif\n+\n /**\n * @brief Deallocate memory.\n * @param __a An allocator.\ndiff --git a/libstdc++-v3/include/bits/new_allocator.h b/libstdc++-v3/include/bits/new_allocator.h\nindex fbe03e392aa..13018036e3a 100644\n--- a/libstdc++-v3/include/bits/new_allocator.h\n+++ b/libstdc++-v3/include/bits/new_allocator.h\n@@ -162,6 +162,49 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n \t return static_cast<_Tp*>(_GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp)));\n }\n \n+#ifdef __glibcxx_allocate_at_least // C++23\n+ [[nodiscard]] constexpr auto\n+ allocate_at_least(size_t __n)\n+ -> std::allocation_result<_Tp*, size_t>\n+ {\n+\tstatic_assert(requires { sizeof(_Tp); },\n+\t \"cannot allocate incomplete types\");\n+\n+\tconst std::size_t __align_mask = __STDCPP_DEFAULT_NEW_ALIGNMENT__ - 1;\n+\n+\tif constexpr (!requires { sizeof(_Tp); })\n+\t return { nullptr, 0 }; // static_assert already failed\n+\telse if (__builtin_expect(__n > this->_M_max_size(), false))\n+\t {\n+\t if (__n > (std::size_t(-1) / sizeof(_Tp)))\n+\t std::__throw_bad_array_new_length();\n+\t std::__throw_bad_alloc();\n+\t }\n+\telse if constexpr (alignof(_Tp) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)\n+\t {\n+\t const std::align_val_t __al = std::align_val_t(alignof(_Tp));\n+\t auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp), __al);\n+\t return { static_cast<_Tp*>(__p), __n };\n+\t }\n+\telse if constexpr (sizeof(_Tp) > __align_mask)\n+\t {\n+\t auto __p = _GLIBCXX_OPERATOR_NEW(__n * sizeof(_Tp));\n+\t return { static_cast<_Tp*>(__p), __n };\n+\t }\n+\telse // See if more _Tp elements can fit into the allocation.\n+\t {\n+\t const auto __need = __n * sizeof(_Tp);\n+\t const auto __ask = (__need + __align_mask) & ~__align_mask;\n+\t const auto __p = _GLIBCXX_OPERATOR_NEW(__ask);\n+\t using _Uchar = const unsigned char;\n+\t static_assert(sizeof(_Tp) <= _Uchar(-1));\n+\t // Use 8-bit division.\n+\t _Uchar __spare = __ask - __need, __size = sizeof(_Tp);\n+\t return { static_cast<_Tp*>(__p), __n + __spare / __size };\n+\t }\n+ }\n+#endif\n+\n // __p is not permitted to be a null pointer.\n _GLIBCXX20_CONSTEXPR void\n deallocate(_Tp* __p, size_type __n __attribute__ ((__unused__)))\ndiff --git a/libstdc++-v3/include/bits/stl_vector.h b/libstdc++-v3/include/bits/stl_vector.h\nindex c4ca214752a..571018cb6f7 100644\n--- a/libstdc++-v3/include/bits/stl_vector.h\n+++ b/libstdc++-v3/include/bits/stl_vector.h\n@@ -317,6 +317,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n get_allocator() const _GLIBCXX_NOEXCEPT\n { return allocator_type(_M_get_Tp_allocator()); }\n \n+ static _GLIBCXX20_CONSTEXPR size_t\n+ _S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT\n+ {\n+\t// std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,\n+\t// and realistically we can't store more than PTRDIFF_MAX/sizeof(T)\n+\t// (even if std::allocator_traits::max_size says we can).\n+\tconst size_t __diffmax =\n+\t __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);\n+\tconst size_t __allocmax =\n+\t __gnu_cxx::__alloc_traits<_Alloc>::max_size(__a);\n+\treturn (std::min)(__diffmax, __allocmax);\n+ }\n+\n #if __cplusplus >= 201103L\n _Vector_base() = default;\n #else\n@@ -381,12 +394,31 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n public:\n _Vector_impl _M_impl;\n \n+ struct _Alloc_result { pointer __ptr; size_t __count; };\n+\n _GLIBCXX20_CONSTEXPR\n- pointer\n- _M_allocate(size_t __n)\n+ _Alloc_result\n+ _M_allocate_at_least(size_t __n)\n {\n \ttypedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;\n-\treturn __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();\n+\t_Alloc_result __r;\n+\tif (__builtin_expect(__n != 0, true))\n+\t {\n+#ifdef __glibcxx_allocate_at_least // C++23\n+\t auto [__ptr, __count] = _Tr::allocate_at_least(_M_impl, __n);\n+\t if (__count > __n)\n+\t {\n+\t\tsize_t __max = _S_max_size(_M_get_Tp_allocator());\n+\t\tif (__builtin_expect(__count > __max, false))\n+\t\t __count = __max;\n+\t }\n+\t __r.__ptr = __ptr, __r.__count = __count;\n+#else\n+\t __r.__ptr = _Tr::allocate(_M_impl, __n), __r.__count = __n;\n+#endif\n+\t }\n+\telse __r.__ptr = pointer(), __r.__count = 0;\n+\treturn __r;\n }\n \n _GLIBCXX20_CONSTEXPR\n@@ -404,9 +436,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n void\n _M_create_storage(size_t __n)\n {\n-\tthis->_M_impl._M_start = this->_M_allocate(__n);\n-\tthis->_M_impl._M_finish = this->_M_impl._M_start;\n-\tthis->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;\n+\t_Alloc_result __r = this->_M_allocate_at_least(__n);\n+\tthis->_M_impl._M_finish = this->_M_impl._M_start = __r.__ptr;\n+\tthis->_M_impl._M_end_of_storage = this->_M_impl._M_start + __r.__count;\n }\n \n #if __glibcxx_containers_ranges // C++ >= 23\n@@ -480,6 +512,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n typedef _Vector_base<_Tp, _Alloc>\t\t\t_Base;\n typedef typename _Base::_Tp_alloc_type\t\t_Tp_alloc_type;\n typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type>\t_Alloc_traits;\n+ typedef typename _Base::_Alloc_result _Alloc_result;\n \n public:\n typedef _Tp\t\t\t\t\tvalue_type;\n@@ -535,7 +568,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n #endif // C++11\n \n protected:\n- using _Base::_M_allocate;\n+ using _Base::_M_allocate_at_least;\n using _Base::_M_deallocate;\n using _Base::_M_impl;\n using _Base::_M_get_Tp_allocator;\n@@ -1116,7 +1149,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n _GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR\n size_type\n max_size() const _GLIBCXX_NOEXCEPT\n- { return _S_max_size(_M_get_Tp_allocator()); }\n+ { return _Base::_S_max_size(_M_get_Tp_allocator()); }\n \n #if __cplusplus >= 201103L\n /**\n@@ -1682,16 +1715,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t\t return;\n \t\t}\n \n-\t const size_type __len = _M_check_len(__n, \"vector::append_range\");\n+\t const size_type __len1 = _M_check_len(__n, \"vector::append_range\");\n \n \t pointer __old_start = this->_M_impl._M_start;\n \t pointer __old_finish = this->_M_impl._M_finish;\n \n-\t allocator_type& __a = _M_get_Tp_allocator();\n-\t const pointer __start = this->_M_allocate(__len);\n+\t auto [__ptr, __count] = this->_M_allocate_at_least(__len1);\n+\t const size_type __len = __count;\n+\t const pointer __start = __ptr;\n \t const pointer __mid = __start + __sz;\n \t const pointer __back = __mid + __n;\n \t _Guard_alloc __guard(__start, __len, *this);\n+\t allocator_type& __a = _M_get_Tp_allocator();\n \t std::__uninitialized_copy_a(ranges::begin(__rg),\n \t\t\t\t\t ranges::end(__rg),\n \t\t\t\t\t __mid, __a);\n@@ -1897,7 +1932,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t_M_allocate_and_copy(size_type __n,\n \t\t\t _ForwardIterator __first, _ForwardIterator __last)\n \t{\n-\t _Guard_alloc __guard(this->_M_allocate(__n), __n, *this);\n+\t _Alloc_result __r = this->_M_allocate_at_least(__n);\n+\t _Guard_alloc __guard(__r.__ptr, __r.__count, *this);\n \t std::__uninitialized_copy_a\n \t (__first, __last, __guard._M_storage, _M_get_Tp_allocator());\n \t return __guard._M_release();\n@@ -1916,8 +1952,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t_M_initialize_dispatch(_Integer __int_n, _Integer __value, __true_type)\n \t{\n \t const size_type __n = static_cast<size_type>(__int_n);\n-\t pointer __start =\n-\t _M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator()));\n+\t pointer __start = _M_allocate_at_least(\n+\t _S_check_init_len(__n, _M_get_Tp_allocator())).__ptr;\n \t this->_M_impl._M_start = __start;\n \t this->_M_impl._M_end_of_storage = __start + __n;\n \t _M_fill_initialize(__n, __value);\n@@ -1971,10 +2007,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t_M_range_initialize_n(_Iterator __first, _Sentinel __last,\n \t\t\t size_type __n)\n \t{\n-\t pointer __start =\n-\t this->_M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator()));\n+\t _Alloc_result __r = this->_M_allocate_at_least(\n+\t _S_check_init_len(__n, _M_get_Tp_allocator()));\n+\t pointer __start = __r.__ptr;\n \t this->_M_impl._M_start = this->_M_impl._M_finish = __start;\n-\t this->_M_impl._M_end_of_storage = __start + __n;\n+\t this->_M_impl._M_end_of_storage = __start + __r.__count;\n \t this->_M_impl._M_finish\n \t = std::__uninitialized_copy_a(_GLIBCXX_MOVE(__first), __last,\n \t\t\t\t\t __start, _M_get_Tp_allocator());\n@@ -2191,35 +2228,27 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n size_type\n _M_check_len(size_type __n, const char* __s) const\n {\n-\tif (max_size() - size() < __n)\n+\tconst size_type __room = max_size() - size();\n+\tif (__room < __n)\n \t __throw_length_error(__N(__s));\n \n-\tconst size_type __len = size() + (std::max)(size(), __n);\n-\treturn (__len < size() || __len > max_size()) ? max_size() : __len;\n+\tif (__n < size())\n+\t __n = size(); // Grow by (at least) doubling ...\n+\tif (__n > __room)\n+\t __n = __room; // ... but only as much as will fit.\n+\treturn size() + __n;\n }\n \n // Called by constructors to check initial size.\n static _GLIBCXX20_CONSTEXPR size_type\n _S_check_init_len(size_type __n, const allocator_type& __a)\n {\n-\tif (__n > _S_max_size(_Tp_alloc_type(__a)))\n+\tif (__n > _Base::_S_max_size(_Tp_alloc_type(__a)))\n \t __throw_length_error(\n \t __N(\"cannot create std::vector larger than max_size()\"));\n \treturn __n;\n }\n \n- static _GLIBCXX20_CONSTEXPR size_type\n- _S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT\n- {\n-\t// std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,\n-\t// and realistically we can't store more than PTRDIFF_MAX/sizeof(T)\n-\t// (even if std::allocator_traits::max_size says we can).\n-\tconst size_t __diffmax\n-\t = __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);\n-\tconst size_t __allocmax = _Alloc_traits::max_size(__a);\n-\treturn (std::min)(__diffmax, __allocmax);\n- }\n-\n // Internal erase functions follow.\n \n // Called by erase(q1,q2), clear(), resize(), _M_fill_assign,\ndiff --git a/libstdc++-v3/include/bits/vector.tcc b/libstdc++-v3/include/bits/vector.tcc\nindex b790fca2964..9524038b7d1 100644\n--- a/libstdc++-v3/include/bits/vector.tcc\n+++ b/libstdc++-v3/include/bits/vector.tcc\n@@ -79,7 +79,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n #if __cplusplus >= 201103L\n \t if constexpr (_S_use_relocate())\n \t {\n-\t __tmp = this->_M_allocate(__n);\n+\t auto __res = this->_M_allocate_at_least(__n);\n+\t __tmp = __res.__ptr;\n+\t __n = __res.__count;\n \t std::__relocate_a(this->_M_impl._M_start, this->_M_impl._M_finish,\n \t\t\t\t__tmp, _M_get_Tp_allocator());\n \t }\n@@ -106,12 +108,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n #if __cplusplus >= 201103L\n template<typename _Tp, typename _Alloc>\n template<typename... _Args>\n-#if __cplusplus > 201402L\n+# if __cplusplus > 201402L\n _GLIBCXX20_CONSTEXPR\n typename vector<_Tp, _Alloc>::reference\n-#else\n+# else\n void\n-#endif\n+# endif\n vector<_Tp, _Alloc>::\n emplace_back(_Args&&... __args)\n {\n@@ -125,11 +127,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t }\n \telse\n \t _M_realloc_append(std::forward<_Args>(__args)...);\n-#if __cplusplus > 201402L\n+# if __cplusplus > 201402L\n \treturn back();\n-#endif\n+# endif\n }\n-#endif\n+#endif // __cplusplus >= 201103L\n \n template<typename _Tp, typename _Alloc>\n _GLIBCXX20_CONSTEXPR\n@@ -464,13 +466,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n _M_realloc_insert(iterator __position, const _Tp& __x)\n #endif\n {\n- const size_type __len = _M_check_len(1u, \"vector::_M_realloc_insert\");\n- if (__len <= 0)\n+ const size_type __len1 = _M_check_len(1u, \"vector::_M_realloc_insert\");\n+ if (__len1 <= 0)\n \t__builtin_unreachable();\n pointer __old_start = this->_M_impl._M_start;\n pointer __old_finish = this->_M_impl._M_finish;\n const size_type __elems_before = __position - begin();\n- pointer __new_start(this->_M_allocate(__len));\n+ _Alloc_result __r = this->_M_allocate_at_least(__len1);\n+ const size_type __len = __r.__count;\n+ pointer __new_start(__r.__ptr);\n pointer __new_finish(__new_start);\n \n {\n@@ -574,14 +578,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n const size_type __len = _M_check_len(1u, \"vector::_M_realloc_append\");\n if (__len <= 0)\n \t__builtin_unreachable();\n- pointer __old_start = this->_M_impl._M_start;\n- pointer __old_finish = this->_M_impl._M_finish;\n+ const pointer __old_start = this->_M_impl._M_start;\n+ const pointer __old_finish = this->_M_impl._M_finish;\n const size_type __elems = size();\n- pointer __new_start(this->_M_allocate(__len));\n+ const _Alloc_result __r = this->_M_allocate_at_least(__len);\n+ const size_type __rlen = __r.__count;\n+ const pointer __new_start(__r.__ptr);\n pointer __new_finish(__new_start);\n \n {\n-\t_Guard_alloc __guard(__new_start, __len, *this);\n+\t_Guard_alloc __guard(__new_start, __rlen, *this);\n \n \t// The order of the three operations is dictated by the C++11\n \t// case, where the moves could alter a new element belonging\n@@ -652,7 +658,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \n this->_M_impl._M_start = __new_start;\n this->_M_impl._M_finish = __new_finish;\n- this->_M_impl._M_end_of_storage = __new_start + __len;\n+ this->_M_impl._M_end_of_storage = __new_start + __rlen;\n }\n #pragma GCC diagnostic pop\n \n@@ -716,10 +722,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t pointer __old_finish = this->_M_impl._M_finish;\n \t const pointer __pos = __position.base();\n \n-\t const size_type __len =\n+\t const size_type __len1 =\n \t\t_M_check_len(__n, \"vector::_M_fill_insert\");\n \t const size_type __elems_before = __pos - __old_start;\n-\t pointer __new_start(this->_M_allocate(__len));\n+\t _Alloc_result __r = this->_M_allocate_at_least(__len1);\n+\t const size_type __len = __r.__count;\n+\t pointer __new_start(__r.__ptr);\n \t pointer __new_finish(__new_start);\n \t __try\n \t\t{\n@@ -787,7 +795,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \n \t const size_type __len =\n \t _M_check_len(__n, \"vector::_M_fill_append\");\n-\t pointer __new_start(this->_M_allocate(__len));\n+\t _Alloc_result __r = this->_M_allocate_at_least(__len);\n+\t __len = __r.__count;\n+\t pointer __new_start(__r.__ptr);\n \t pointer __new_finish(__new_start + __old_size);\n \t __try\n \t {\n@@ -852,9 +862,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t pointer __old_start = this->_M_impl._M_start;\n \t pointer __old_finish = this->_M_impl._M_finish;\n \n-\t const size_type __len =\n+\t const size_type __len1 =\n \t\t_M_check_len(__n, \"vector::_M_default_append\");\n-\t pointer __new_start(this->_M_allocate(__len));\n+\t _Alloc_result __r = this->_M_allocate_at_least(__len1);\n+\t const size_type __len = __r.__count;\n+\t pointer __new_start(__r.__ptr);\n \n \t {\n \t\t_Guard_alloc __guard(__new_start, __len, *this);\n@@ -1003,14 +1015,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t\tpointer __old_start = this->_M_impl._M_start;\n \t\tpointer __old_finish = this->_M_impl._M_finish;\n \n-\t\tconst size_type __len =\n+\t\tconst size_type __len1 =\n \t\t _M_check_len(__n, \"vector::_M_range_insert\");\n #if __cplusplus < 201103L\n-\t\tif (__len < (__n + (__old_finish - __old_start)))\n+\t\tif (__len1 < (__n + (__old_finish - __old_start)))\n \t\t __builtin_unreachable();\n #endif\n \n-\t\tpointer __new_start(this->_M_allocate(__len));\n+\t\t_Alloc_result __r = this->_M_allocate_at_least(__len1);\n+\t\tconst size_type __len = __r.__count;\n+\t\tpointer __new_start(__r.__ptr);\n \t\tpointer __new_finish(__new_start);\n \t\t__try\n \t\t {\n@@ -1111,7 +1125,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t }\n \t else // Reallocate\n \t {\n-\t\tconst size_type __len\n+\t\tconst size_type __len1\n \t\t = _M_check_len(__n, \"vector::insert_range\");\n \n \t\tstruct _Guard : _Guard_alloc\n@@ -1130,7 +1144,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER\n \t\t};\n \n \t\t// Allocate new storage:\n-\t\tpointer __new_start(this->_M_allocate(__len));\n+\t\t_Alloc_result __r = this->_M_allocate_at_least(__len1);\n+\t\tconst size_type __len = __r.__count;\n+\t\tpointer __new_start(__r.__ptr);\n \t\t_Guard __guard(__new_start, __len, *this);\n \n \t\tauto& __alloc = _M_get_Tp_allocator();\ndiff --git a/libstdc++-v3/include/std/string b/libstdc++-v3/include/std/string\nindex c2b37391fc7..da50bce2e7d 100644\n--- a/libstdc++-v3/include/std/string\n+++ b/libstdc++-v3/include/std/string\n@@ -67,6 +67,7 @@\n #endif\n \n #define __glibcxx_want_algorithm_default_value_type\n+#define __glibcxx_want_allocate_at_least\n #define __glibcxx_want_allocator_traits_is_always_equal\n #define __glibcxx_want_constexpr_char_traits\n #define __glibcxx_want_constexpr_string\ndiff --git a/libstdc++-v3/include/std/vector b/libstdc++-v3/include/std/vector\nindex 343483e9519..ac041cc9408 100644\n--- a/libstdc++-v3/include/std/vector\n+++ b/libstdc++-v3/include/std/vector\n@@ -79,6 +79,7 @@\n #endif\n \n #define __glibcxx_want_algorithm_default_value_type\n+#define __glibcxx_want_allocate_at_least\n #define __glibcxx_want_allocator_traits_is_always_equal\n #define __glibcxx_want_constexpr_vector\n #define __glibcxx_want_containers_ranges\ndiff --git a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc\nindex 44c8391ebc2..9942e1cfb6a 100644\n--- a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc\n+++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/constexpr.cc\n@@ -34,7 +34,7 @@ struct Alloc : std::allocator<T>\n { return personality == a.personality; }\n };\n \n-constexpr bool\n+consteexpr bool\n test_default_ctor()\n {\n std::basic_string<C> s0;\ndiff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/libstdc++-v3/testsuite/util/testsuite_allocator.h\nindex 892a385e307..d8914229c7a 100644\n--- a/libstdc++-v3/testsuite/util/testsuite_allocator.h\n+++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h\n@@ -176,6 +176,16 @@ namespace __gnu_test\n \treturn p;\n }\n \n+#ifdef __glibcxx_allocate_at_least\n+ std::allocation_result<pointer, size_type>\n+ allocate_at_least(size_type n)\n+ {\n+\tauto r = AllocTraits::allocate_at_least(*this, n);\n+\tcounter_type::allocate(r.count * sizeof(T));\n+\treturn r;\n+ }\n+#endif\n+\n #if __cplusplus >= 201103L\n template<typename U, typename... Args>\n \tvoid\n@@ -353,6 +363,9 @@ namespace __gnu_test\n _GLIBCXX20_CONSTEXPR\n pointer\n allocate(size_type n, const void* = 0)\n+#ifdef __glibcxx_allocate_at_least\n+ { return this->allocate_at_least(n).ptr; }\n+#else\n {\n \tpointer p = AllocTraits::allocate(*this, n);\n \n@@ -372,6 +385,33 @@ namespace __gnu_test\n \n \treturn p;\n }\n+#endif\n+\n+#ifdef __glibcxx_allocate_at_least\n+ _GLIBCXX20_CONSTEXPR\n+ auto\n+ allocate_at_least(size_type n)\n+ -> std::allocation_result<Tp*, size_t>\n+ {\n+\tauto r = AllocTraits::allocate_at_least(*this, n);\n+\n+\tif (std::__is_constant_evaluated())\n+\t return r;\n+\n+\ttry\n+\t {\n+\t get_map().insert(map_type::value_type(\n+\t\t reinterpret_cast<void*>(r.ptr), personality));\n+\t }\n+\tcatch(...)\n+\t {\n+\t AllocTraits::deallocate(*this, r.ptr, r.count);\n+\t __throw_exception_again;\n+\t }\n+\n+\treturn r;\n+ }\n+#endif\n \n _GLIBCXX14_CONSTEXPR\n void\n", "prefixes": [ "RFC" ] }