[libcxx-commits] [libcxx] [libc++] P3391R2: `constexpr` integral overloads of `to_(w)string` (PR #205025)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Tue Jun 23 01:24:38 PDT 2026


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/205025

>From fda1aaf3bf91965678b844e5c8e736a2cec12df0 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Mon, 22 Jun 2026 11:17:25 +0800
Subject: [PATCH 1/6] [libc++] P3391R2: `constexpr` integral overloads of
 `to_(w)string`

Also implements related LWG4531.
---
 libcxx/docs/FeatureTestMacroTable.rst         |  2 +
 libcxx/docs/Status/Cxx26Papers.csv            |  2 +-
 libcxx/docs/Status/Cxx29Issues.csv            |  2 +-
 libcxx/include/string                         | 55 +++++++++++
 libcxx/include/version                        |  5 +-
 libcxx/src/string.cpp                         | 50 +++++-----
 .../test/libcxx/transitive_includes/cxx26.csv |  7 ++
 .../string.version.compile.pass.cpp           |  4 +-
 .../version.version.compile.pass.cpp          |  4 +-
 .../string.conversions/to_string.pass.cpp     | 15 ++-
 .../string.conversions/to_wstring.pass.cpp    | 15 ++-
 libcxx/test/support/parse_integer.h           | 96 +++++++++++++++----
 .../generate_feature_test_macro_components.py |  2 +-
 13 files changed, 201 insertions(+), 58 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 8e26bdcd860c1..3baa0acb5c06a 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -450,6 +450,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_queue``                              ``202502L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_constexpr_string``                             ``202511L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_constrained_equality``                         ``202411L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_copyable_function``                            *unimplemented*
diff --git a/libcxx/docs/Status/Cxx26Papers.csv b/libcxx/docs/Status/Cxx26Papers.csv
index a951ac4c6c833..8dce1ed74811a 100644
--- a/libcxx/docs/Status/Cxx26Papers.csv
+++ b/libcxx/docs/Status/Cxx26Papers.csv
@@ -175,7 +175,7 @@
 "`P3887R1 <https://wg21.link/P3887R1>`__","Make ``when_all`` a Ronseal Algorithm","2025-11 (Kona)","","","`#171289 <https://github.com/llvm/llvm-project/issues/171289>`__",""
 "`P3923R0 <https://wg21.link/P3923R0>`__","Additional NB comment resolutions for Kona 2025","2025-11 (Kona)","","","`#171290 <https://github.com/llvm/llvm-project/issues/171290>`__",""
 "`P3371R5 <https://wg21.link/P3371R5>`__","Fix C++26 by making the rank-1, rank-2, rank-k, and rank-2k updates consistent with the BLAS","2025-11 (Kona)","","","`#171291 <https://github.com/llvm/llvm-project/issues/171291>`__",""
-"`P3391R2 <https://wg21.link/P3391R2>`__","``constexpr`` ``std::format``","2025-11 (Kona)","","","`#171292 <https://github.com/llvm/llvm-project/issues/171292>`__",""
+"`P3391R2 <https://wg21.link/P3391R2>`__","``constexpr`` ``std::format``","2025-11 (Kona)","|Partial|","","`#171292 <https://github.com/llvm/llvm-project/issues/171292>`__","``std::to_(w)string`` parts are implemented in LLVM 23."
 "`P3913R1 <https://wg21.link/P3913R1>`__","Optimize ``for std::optional`` in range adaptors","2025-11 (Kona)","","","`#171294 <https://github.com/llvm/llvm-project/issues/171294>`__",""
 "","","","","","",""
 "`P3726R2 <https://wg21.link/P3726R2>`__","Adjustments to Union Lifetime Rules","2026-03 (Croydon)","","","`#189560 <https://github.com/llvm/llvm-project/issues/189560>`__",""
diff --git a/libcxx/docs/Status/Cxx29Issues.csv b/libcxx/docs/Status/Cxx29Issues.csv
index d9e245a9a11c0..072ab8effa121 100644
--- a/libcxx/docs/Status/Cxx29Issues.csv
+++ b/libcxx/docs/Status/Cxx29Issues.csv
@@ -13,7 +13,7 @@
 "`LWG4487 <https://wg21.link/LWG4487>`__","Is member ``is_steady`` of a *Cpp17Clock* type required to be usable in constant expressions?","2026-06 (Brno)","","","`#204451 <https://github.com/llvm/llvm-project/issues/204451>`__",""
 "`LWG4490 <https://wg21.link/LWG4490>`__","Allow calling ``std::ranges::size`` in ranges algorithms","2026-06 (Brno)","","","`#204452 <https://github.com/llvm/llvm-project/issues/204452>`__",""
 "`LWG4521 <https://wg21.link/LWG4521>`__","Improve [atomics.order] p10 to have a consistent way with [intro.races]","2026-06 (Brno)","","","`#204453 <https://github.com/llvm/llvm-project/issues/204453>`__",""
-"`LWG4531 <https://wg21.link/LWG4531>`__","Should there be a feature-test macro update for ``constexpr`` ``std::to_(w)string``?","2026-06 (Brno)","","","`#204454 <https://github.com/llvm/llvm-project/issues/204454>`__",""
+"`LWG4531 <https://wg21.link/LWG4531>`__","Should there be a feature-test macro update for ``constexpr`` ``std::to_(w)string``?","2026-06 (Brno)","|Complete|","23","`#204454 <https://github.com/llvm/llvm-project/issues/204454>`__",""
 "`LWG4567 <https://wg21.link/LWG4567>`__","Feature test macro value for ``apply_result``, ``is_applicable``","2026-06 (Brno)","","","`#204455 <https://github.com/llvm/llvm-project/issues/204455>`__",""
 "`LWG4568 <https://wg21.link/LWG4568>`__","``std::execution::spawn_future`` is mishandling dependent senders","2026-06 (Brno)","","","`#204456 <https://github.com/llvm/llvm-project/issues/204456>`__",""
 "`LWG4579 <https://wg21.link/LWG4579>`__","``make-state<Rcvr>::state-type::complete`` uses ``Env`` which is not in scope","2026-06 (Brno)","","","`#204457 <https://github.com/llvm/llvm-project/issues/204457>`__",""
diff --git a/libcxx/include/string b/libcxx/include/string
index e5bc854cd34f3..d1217b65475bd 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -663,6 +663,12 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #    include <cwchar>
 #  endif
 
+#  if _LIBCPP_STD_VER >= 26
+#    include <__charconv/to_chars_integral.h>
+#    include <__charconv/to_chars_result.h>
+#    include <__system_error/errc.h>
+#  endif
+
 // standard-mandated includes
 
 // [iterator.range]
@@ -3774,12 +3780,41 @@ stoull(const string& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const string& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const string& __str, size_t* __idx = nullptr);
 
+#  if _LIBCPP_STD_VER >= 26
+
+template <class _String, class _Integer>
+constexpr _String __integer_to_string(_Integer __val) {
+  // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
+  // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented), so we need +1 here.
+  constexpr size_t __buf_size = numeric_limits<_Integer>::digits10 + 2; // +1 for minus, +1 for digits10
+  char __buf[__buf_size];
+  const auto [__ptr, __ec] = std::to_chars(__buf, __buf + __buf_size, __val);
+  _LIBCPP_ASSERT_INTERNAL(__ec == errc(), "__buf_size must be large enough to accommodate the value");
+  return _String(__buf, __ptr);
+}
+
+inline namespace __cpp26 {
+
+[[nodiscard]] constexpr string to_string(int __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(unsigned __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(long __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(unsigned long __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(long long __val) { return std::__integer_to_string<string>(__val); }
+[[nodiscard]] constexpr string to_string(unsigned long long __val) { return std::__integer_to_string<string>(__val); }
+
+} // namespace __cpp26
+
+#  else // _LIBCPP_STD_VER >= 26
+
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long __val);
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(double __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long double __val);
@@ -3798,12 +3833,32 @@ stoull(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const wstring& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const wstring& __str, size_t* __idx = nullptr);
 
+#    if _LIBCPP_STD_VER >= 26
+
+inline namespace __cpp26 {
+
+[[nodiscard]] constexpr wstring to_wstring(int __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(unsigned __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(long __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(unsigned long __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(long long __val) { return std::__integer_to_string<wstring>(__val); }
+[[nodiscard]] constexpr wstring to_wstring(unsigned long long __val) {
+  return std::__integer_to_string<wstring>(__val);
+}
+
+} // namespace __cpp26
+
+#    else // _LIBCPP_STD_VER >= 26
+
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long __val);
+
+#    endif // _LIBCPP_STD_VER >= 26
+
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(double __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long double __val);
diff --git a/libcxx/include/version b/libcxx/include/version
index 7f2dc9e4b72ab..ed278dff0f59f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -84,7 +84,8 @@ __cpp_lib_constexpr_memory                              202202L <memory>
 __cpp_lib_constexpr_new                                 202406L <new>
 __cpp_lib_constexpr_numeric                             201911L <numeric>
 __cpp_lib_constexpr_queue                               202502L <queue>
-__cpp_lib_constexpr_string                              201907L <string>
+__cpp_lib_constexpr_string                              202511L <string>
+                                                        201907L // C++20
 __cpp_lib_constexpr_string_view                         201811L <string_view>
 __cpp_lib_constexpr_tuple                               201811L <tuple>
 __cpp_lib_constexpr_typeinfo                            202106L <typeinfo>
@@ -575,6 +576,8 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #   define __cpp_lib_constexpr_new                      202406L
 # endif
 # define __cpp_lib_constexpr_queue                      202502L
+# undef  __cpp_lib_constexpr_string
+# define __cpp_lib_constexpr_string                     202511L
 # define __cpp_lib_constrained_equality                 202411L
 // # define __cpp_lib_copyable_function                    202306L
 // # define __cpp_lib_debugging                            202311L
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index b55f9ff5c9a43..71a0689eecc17 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -328,34 +328,36 @@ inline wide_printf get_swprintf() {
 }
 #endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-template <typename S, typename V>
-S i_to_string(V v) {
-  //  numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
-  //  For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented),
-  //  so we need +1 here.
-  constexpr size_t bufsize = numeric_limits<V>::digits10 + 2; // +1 for minus, +1 for digits10
-  char buf[bufsize];
-  const auto res = to_chars(buf, buf + bufsize, v);
-  _LIBCPP_ASSERT_INTERNAL(res.ec == errc(), "bufsize must be large enough to accomodate the value");
-  return S(buf, res.ptr);
-}
-
 } // unnamed namespace
 
-string to_string(int val) { return i_to_string< string>(val); }
-string to_string(long val) { return i_to_string< string>(val); }
-string to_string(long long val) { return i_to_string< string>(val); }
-string to_string(unsigned val) { return i_to_string< string>(val); }
-string to_string(unsigned long val) { return i_to_string< string>(val); }
-string to_string(unsigned long long val) { return i_to_string< string>(val); }
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long);
+
+string to_string(int val) { return __cpp26::to_string(val); }
+string to_string(long val) { return __cpp26::to_string(val); }
+string to_string(long long val) { return __cpp26::to_string(val); }
+string to_string(unsigned val) { return __cpp26::to_string(val); }
+string to_string(unsigned long val) { return __cpp26::to_string(val); }
+string to_string(unsigned long long val) { return __cpp26::to_string(val); }
 
 #if _LIBCPP_HAS_WIDE_CHARACTERS
-wstring to_wstring(int val) { return i_to_string<wstring>(val); }
-wstring to_wstring(long val) { return i_to_string<wstring>(val); }
-wstring to_wstring(long long val) { return i_to_string<wstring>(val); }
-wstring to_wstring(unsigned val) { return i_to_string<wstring>(val); }
-wstring to_wstring(unsigned long val) { return i_to_string<wstring>(val); }
-wstring to_wstring(unsigned long long val) { return i_to_string<wstring>(val); }
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long);
+[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long);
+
+wstring to_wstring(int val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(long val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(long long val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(unsigned val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(unsigned long val) { return __cpp26::to_wstring(val); }
+wstring to_wstring(unsigned long long val) { return __cpp26::to_wstring(val); }
 #endif
 
 string to_string(float val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 1450a13e125ef..df9510c63b8d4 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -45,6 +45,7 @@ bit cstdint
 bit limits
 bit version
 bitset cctype
+bitset cerrno
 bitset climits
 bitset compare
 bitset cstdint
@@ -130,6 +131,7 @@ cinttypes cstdint
 cmath limits
 cmath version
 codecvt cctype
+codecvt cerrno
 codecvt climits
 codecvt clocale
 codecvt compare
@@ -719,6 +721,7 @@ print tuple
 print typeinfo
 print version
 queue cctype
+queue cerrno
 queue climits
 queue compare
 queue cstdint
@@ -737,6 +740,7 @@ queue tuple
 queue vector
 queue version
 random cctype
+random cerrno
 random climits
 random cmath
 random compare
@@ -780,6 +784,7 @@ ratio climits
 ratio cstdint
 ratio version
 regex cctype
+regex cerrno
 regex climits
 regex clocale
 regex compare
@@ -905,6 +910,7 @@ streambuf tuple
 streambuf typeinfo
 streambuf version
 string cctype
+string cerrno
 string climits
 string compare
 string cstdint
@@ -1083,6 +1089,7 @@ variant initializer_list
 variant limits
 variant version
 vector cctype
+vector cerrno
 vector climits
 vector compare
 vector cstdint
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
index 147854eead2cc..324dc4dbe3200 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp
@@ -417,8 +417,8 @@
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++26"
 #  endif
-#  if __cpp_lib_constexpr_string != 201907L
-#    error "__cpp_lib_constexpr_string should have the value 201907L in c++26"
+#  if __cpp_lib_constexpr_string != 202511L
+#    error "__cpp_lib_constexpr_string should have the value 202511L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_containers_ranges
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index d9c78b73f7e23..aed1162977efc 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -6747,8 +6747,8 @@
 #  ifndef __cpp_lib_constexpr_string
 #    error "__cpp_lib_constexpr_string should be defined in c++26"
 #  endif
-#  if __cpp_lib_constexpr_string != 201907L
-#    error "__cpp_lib_constexpr_string should have the value 201907L in c++26"
+#  if __cpp_lib_constexpr_string != 202511L
+#    error "__cpp_lib_constexpr_string should have the value 202511L in c++26"
 #  endif
 
 #  ifndef __cpp_lib_constexpr_string_view
diff --git a/libcxx/test/std/strings/string.conversions/to_string.pass.cpp b/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
index 4731a072e8910..a36d21592b08d 100644
--- a/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
@@ -26,7 +26,7 @@
 #include "test_macros.h"
 
 template <class T>
-void test_signed() {
+TEST_CONSTEXPR_CXX26 void test_signed() {
   {
     std::string s = std::to_string(T(0));
     assert(s.size() == 1);
@@ -59,7 +59,7 @@ void test_signed() {
 }
 
 template <class T>
-void test_unsigned() {
+TEST_CONSTEXPR_CXX26 void test_unsigned() {
   {
     std::string s = std::to_string(T(0));
     assert(s.size() == 1);
@@ -102,16 +102,25 @@ void test_float() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test_integers() {
   test_signed<int>();
   test_signed<long>();
   test_signed<long long>();
   test_unsigned<unsigned>();
   test_unsigned<unsigned long>();
   test_unsigned<unsigned long long>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test_integers();
   test_float<float>();
   test_float<double>();
   test_float<long double>();
+#if TEST_STD_VER >= 26
+  static_assert(test_integers());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index fff5ede848b57..22d06bffb84b8 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -28,7 +28,7 @@
 #include "test_macros.h"
 
 template <class T>
-void test_signed() {
+TEST_CONSTEXPR_CXX26 void test_signed() {
   {
     std::wstring s = std::to_wstring(T(0));
     assert(s.size() == 1);
@@ -61,7 +61,7 @@ void test_signed() {
 }
 
 template <class T>
-void test_unsigned() {
+TEST_CONSTEXPR_CXX26 void test_unsigned() {
   {
     std::wstring s = std::to_wstring(T(0));
     assert(s.size() == 1);
@@ -104,16 +104,25 @@ void test_float() {
   }
 }
 
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test_integers() {
   test_signed<int>();
   test_signed<long>();
   test_signed<long long>();
   test_unsigned<unsigned>();
   test_unsigned<unsigned long>();
   test_unsigned<unsigned long long>();
+
+  return true;
+}
+
+int main(int, char**) {
+  test_integers();
   test_float<float>();
   test_float<double>();
   test_float<long double>();
+#if TEST_STD_VER >= 26
+  static_assert(test_integers());
+#endif
 
   return 0;
 }
diff --git a/libcxx/test/support/parse_integer.h b/libcxx/test/support/parse_integer.h
index 36f8f7b79881b..d6144b17f57c5 100644
--- a/libcxx/test/support/parse_integer.h
+++ b/libcxx/test/support/parse_integer.h
@@ -9,64 +9,120 @@
 #ifndef LIBCPP_TEST_SUPPORT_PARSE_INTEGER_H
 #define LIBCPP_TEST_SUPPORT_PARSE_INTEGER_H
 
+#include <charconv>
+#include <cstdlib>
 #include <string>
+#include <system_error>
+#include <type_traits>
+
+#include "test_macros.h"
 
 namespace detail {
 template <class T>
 struct parse_integer_impl;
 
+#if TEST_STD_VER >= 23
+template <class I, class CharT>
+consteval I consteval_parse_integer(const std::basic_string<CharT>& str) {
+  I n;
+  auto [ptr, ec] = [&n, &str] {
+    if constexpr (std::is_same_v<CharT, char>) {
+      return std::from_chars(str.data(), str.data() + str.size(), n);
+    } else {
+      std::string s;
+      for (auto c : str)
+        s.push_back(static_cast<char>(c));
+      return std::from_chars(s.data(), s.data() + s.size(), n);
+    }
+  }();
+  if (ec != std::errc{})
+    std::abort();
+  return n;
+}
+#endif
+
 template <>
 struct parse_integer_impl<int> {
-    template <class CharT>
-    int operator()(std::basic_string<CharT> const& str) const {
-        return std::stoi(str);
+  template <class CharT>
+  TEST_CONSTEXPR_CXX23 int operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+    if consteval {
+      return consteval_parse_integer<int>(str);
     }
+#endif
+    return std::stoi(str);
+  }
 };
 
 template <>
 struct parse_integer_impl<long> {
-    template <class CharT>
-    long operator()(std::basic_string<CharT> const& str) const {
-        return std::stol(str);
+  template <class CharT>
+  TEST_CONSTEXPR_CXX23 long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+    if consteval {
+      return consteval_parse_integer<long>(str);
     }
+#endif
+    return std::stol(str);
+  }
 };
 
 template <>
 struct parse_integer_impl<long long> {
-    template <class CharT>
-    long long operator()(std::basic_string<CharT> const& str) const {
-        return std::stoll(str);
+  template <class CharT>
+  TEST_CONSTEXPR_CXX23 long long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+    if consteval {
+      return consteval_parse_integer<long long>(str);
     }
+#endif
+    return std::stoll(str);
+  }
 };
 
 template <>
 struct parse_integer_impl<unsigned int> {
-    template <class CharT>
-    unsigned int operator()(std::basic_string<CharT> const& str) const {
-        return std::stoul(str);
+  template <class CharT>
+  TEST_CONSTEXPR_CXX23 unsigned int operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+    if consteval {
+      return consteval_parse_integer<unsigned int>(str);
     }
+#endif
+    return std::stoul(str);
+  }
 };
 
 template <>
 struct parse_integer_impl<unsigned long> {
-    template <class CharT>
-    unsigned long operator()(std::basic_string<CharT> const& str) const {
-        return std::stoul(str);
+  template <class CharT>
+  TEST_CONSTEXPR_CXX23 unsigned long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+    if consteval {
+      return consteval_parse_integer<unsigned long>(str);
     }
+#endif
+    return std::stoul(str);
+  }
 };
 
 template <>
 struct parse_integer_impl<unsigned long long> {
-    template <class CharT>
-    unsigned long long operator()(std::basic_string<CharT> const& str) const {
-        return std::stoull(str);
+  template <class CharT>
+  TEST_CONSTEXPR_CXX23 unsigned long long operator()(std::basic_string<CharT> const& str) const {
+#if TEST_STD_VER >= 23
+    if consteval {
+      return consteval_parse_integer<unsigned long long>(str);
     }
+#endif
+    return std::stoull(str);
+  }
 };
 } // namespace detail
 
 template <class T, class CharT>
-T parse_integer(std::basic_string<CharT> const& str) {
-    return detail::parse_integer_impl<T>()(str);
+TEST_CONSTEXPR_CXX23 T parse_integer(std::basic_string<CharT> const& str) {
+  return detail::parse_integer_impl<T>()(str);
 }
 
 #endif // LIBCPP_TEST_SUPPORT_PARSE_INTEGER_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d764a1f677ba6..1f4961fda4d6f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -434,7 +434,7 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_constexpr_string",
-            "values": {"c++20": 201907},
+            "values": {"c++20": 201907, "c++26": 202511},
             "headers": ["string"],
         },
         {

>From 4baebb05bb50957edce254c8d93947a07876644f Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 23 Jun 2026 09:41:44 +0800
Subject: [PATCH 2/6] Use `_LIBCPP_PREFERRED_OVERLOAD` as suggested

---
 libcxx/include/string | 154 +++++++++++++++++++++++++++++-------------
 libcxx/src/string.cpp |  32 +++++----
 2 files changed, 125 insertions(+), 61 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index d1217b65475bd..72ff5fe70d971 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -3780,40 +3780,14 @@ stoull(const string& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const string& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const string& __str, size_t* __idx = nullptr);
 
-#  if _LIBCPP_STD_VER >= 26
-
-template <class _String, class _Integer>
-constexpr _String __integer_to_string(_Integer __val) {
-  // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
-  // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented), so we need +1 here.
-  constexpr size_t __buf_size = numeric_limits<_Integer>::digits10 + 2; // +1 for minus, +1 for digits10
-  char __buf[__buf_size];
-  const auto [__ptr, __ec] = std::to_chars(__buf, __buf + __buf_size, __val);
-  _LIBCPP_ASSERT_INTERNAL(__ec == errc(), "__buf_size must be large enough to accommodate the value");
-  return _String(__buf, __ptr);
-}
-
-inline namespace __cpp26 {
-
-[[nodiscard]] constexpr string to_string(int __val) { return std::__integer_to_string<string>(__val); }
-[[nodiscard]] constexpr string to_string(unsigned __val) { return std::__integer_to_string<string>(__val); }
-[[nodiscard]] constexpr string to_string(long __val) { return std::__integer_to_string<string>(__val); }
-[[nodiscard]] constexpr string to_string(unsigned long __val) { return std::__integer_to_string<string>(__val); }
-[[nodiscard]] constexpr string to_string(long long __val) { return std::__integer_to_string<string>(__val); }
-[[nodiscard]] constexpr string to_string(unsigned long long __val) { return std::__integer_to_string<string>(__val); }
-
-} // namespace __cpp26
-
-#  else // _LIBCPP_STD_VER >= 26
-
+#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long __val);
-
-#  endif // _LIBCPP_STD_VER >= 26
+#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
 
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(double __val);
@@ -3833,37 +3807,123 @@ stoull(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const wstring& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const wstring& __str, size_t* __idx = nullptr);
 
-#    if _LIBCPP_STD_VER >= 26
-
-inline namespace __cpp26 {
-
-[[nodiscard]] constexpr wstring to_wstring(int __val) { return std::__integer_to_string<wstring>(__val); }
-[[nodiscard]] constexpr wstring to_wstring(unsigned __val) { return std::__integer_to_string<wstring>(__val); }
-[[nodiscard]] constexpr wstring to_wstring(long __val) { return std::__integer_to_string<wstring>(__val); }
-[[nodiscard]] constexpr wstring to_wstring(unsigned long __val) { return std::__integer_to_string<wstring>(__val); }
-[[nodiscard]] constexpr wstring to_wstring(long long __val) { return std::__integer_to_string<wstring>(__val); }
-[[nodiscard]] constexpr wstring to_wstring(unsigned long long __val) {
-  return std::__integer_to_string<wstring>(__val);
-}
-
-} // namespace __cpp26
-
-#    else // _LIBCPP_STD_VER >= 26
-
+#    if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long __val);
-
-#    endif // _LIBCPP_STD_VER >= 26
+#    endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
 
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(double __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long double __val);
 #  endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
+#  if _LIBCPP_STD_VER >= 26
+
+// Note: _LIBCPP_ALWAYS_INLINE is used for generating direct invocations to dylib versions for Clang,
+// which avoids code size regression in C++26.
+
+template <class _String, class _Integer>
+constexpr _String __integer_to_string(_Integer __val) {
+  // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
+  // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented), so we need +1 here.
+  constexpr size_t __buf_size = numeric_limits<_Integer>::digits10 + 2; // +1 for minus, +1 for digits10
+  char __buf[__buf_size];
+  const auto [__ptr, __ec] = std::to_chars(__buf, __buf + __buf_size, __val);
+  _LIBCPP_ASSERT_INTERNAL(__ec == errc(), "__buf_size must be large enough to accommodate the value");
+  return _String(__buf, __ptr);
+}
+
+template <class _Integer>
+_LIBCPP_ALWAYS_INLINE constexpr string __to_string(_Integer __val) {
+#    if _LIBCPP_PREFERRED_OVERLOAD
+  if !consteval {
+    return std::to_string(__val);
+  } else
+#    endif // _LIBCPP_PREFERRED_OVERLOAD
+  {
+    return std::__integer_to_string<string>(__val);
+  }
+}
+
+#    if _LIBCPP_HAS_WIDE_CHARACTERS
+template <class _Integer>
+_LIBCPP_ALWAYS_INLINE constexpr wstring __to_wstring(_Integer __val) {
+#      if _LIBCPP_PREFERRED_OVERLOAD
+  if !consteval {
+    return std::to_wstring(__val);
+  } else
+#      endif // _LIBCPP_PREFERRED_OVERLOAD
+  {
+    return std::__integer_to_string<wstring>(__val);
+  }
+}
+#    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
+
+inline namespace __cpp26 {
+
+#    ifdef _LIBCPP_PREFERRED_OVERLOAD
+#      define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE _LIBCPP_PREFERRED_OVERLOAD
+#    else // _LIBCPP_PREFERRED_OVERLOAD
+#      define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE
+#    endif // _LIBCPP_PREFERRED_OVERLOAD
+
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(int __val) {
+  return std::__to_string(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(unsigned __val) {
+  return std::__to_string(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(long __val) {
+  return std::__to_string(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
+to_string(unsigned long __val) {
+  return std::__to_string(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
+to_string(long long __val) {
+  return std::__to_string(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
+to_string(unsigned long long __val) {
+  return std::__to_string(__val);
+}
+
+#    if _LIBCPP_HAS_WIDE_CHARACTERS
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring to_wstring(int __val) {
+  return std::__to_wstring(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+to_wstring(unsigned __val) {
+  return std::__to_wstring(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring to_wstring(long __val) {
+  return std::__to_wstring(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+to_wstring(unsigned long __val) {
+  return std::__to_wstring(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+to_wstring(long long __val) {
+  return std::__to_wstring(__val);
+}
+[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+to_wstring(unsigned long long __val) {
+  return std::__to_wstring(__val);
+}
+#    endif // _LIBCPP_HAS_WIDE_CHARACTERS
+
+#    undef _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE
+
+} // namespace __cpp26
+
+#  endif // _LIBCPP_STD_VER >= 26
+
 template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_TEMPLATE_DATA_VIS const typename basic_string<_CharT, _Traits, _Allocator>::size_type
     basic_string<_CharT, _Traits, _Allocator>::npos;
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index 71a0689eecc17..812c4080650f0 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -330,6 +330,7 @@ inline wide_printf get_swprintf() {
 
 } // unnamed namespace
 
+#ifndef _LIBCPP_PREFERRED_OVERLOAD
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long);
@@ -337,27 +338,30 @@ inline wide_printf get_swprintf() {
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long);
 
-string to_string(int val) { return __cpp26::to_string(val); }
-string to_string(long val) { return __cpp26::to_string(val); }
-string to_string(long long val) { return __cpp26::to_string(val); }
-string to_string(unsigned val) { return __cpp26::to_string(val); }
-string to_string(unsigned long val) { return __cpp26::to_string(val); }
-string to_string(unsigned long long val) { return __cpp26::to_string(val); }
-
-#if _LIBCPP_HAS_WIDE_CHARACTERS
+#  if _LIBCPP_HAS_WIDE_CHARACTERS
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long);
+#  endif
+#endif
+
+string to_string(int val) { return std::__integer_to_string<string>(val); }
+string to_string(long val) { return std::__integer_to_string<string>(val); }
+string to_string(long long val) { return std::__integer_to_string<string>(val); }
+string to_string(unsigned val) { return std::__integer_to_string<string>(val); }
+string to_string(unsigned long val) { return std::__integer_to_string<string>(val); }
+string to_string(unsigned long long val) { return std::__integer_to_string<string>(val); }
 
-wstring to_wstring(int val) { return __cpp26::to_wstring(val); }
-wstring to_wstring(long val) { return __cpp26::to_wstring(val); }
-wstring to_wstring(long long val) { return __cpp26::to_wstring(val); }
-wstring to_wstring(unsigned val) { return __cpp26::to_wstring(val); }
-wstring to_wstring(unsigned long val) { return __cpp26::to_wstring(val); }
-wstring to_wstring(unsigned long long val) { return __cpp26::to_wstring(val); }
+#if _LIBCPP_HAS_WIDE_CHARACTERS
+wstring to_wstring(int val) { return std::__integer_to_string<wstring>(val); }
+wstring to_wstring(long val) { return std::__integer_to_string<wstring>(val); }
+wstring to_wstring(long long val) { return std::__integer_to_string<wstring>(val); }
+wstring to_wstring(unsigned val) { return std::__integer_to_string<wstring>(val); }
+wstring to_wstring(unsigned long val) { return std::__integer_to_string<wstring>(val); }
+wstring to_wstring(unsigned long long val) { return std::__integer_to_string<wstring>(val); }
 #endif
 
 string to_string(float val) { return as_string(snprintf, initial_string< string>()(), "%f", val); }

>From 2da099bade0b095a8ced5dc4e20452e46e41e44f Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 23 Jun 2026 09:45:01 +0800
Subject: [PATCH 3/6] Fixup `#ifdef _LIBCPP_PREFERRED_OVERLOAD`

---
 libcxx/include/string | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 72ff5fe70d971..61e347c34075c 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -3839,7 +3839,7 @@ constexpr _String __integer_to_string(_Integer __val) {
 
 template <class _Integer>
 _LIBCPP_ALWAYS_INLINE constexpr string __to_string(_Integer __val) {
-#    if _LIBCPP_PREFERRED_OVERLOAD
+#    ifdef _LIBCPP_PREFERRED_OVERLOAD
   if !consteval {
     return std::to_string(__val);
   } else
@@ -3852,7 +3852,7 @@ _LIBCPP_ALWAYS_INLINE constexpr string __to_string(_Integer __val) {
 #    if _LIBCPP_HAS_WIDE_CHARACTERS
 template <class _Integer>
 _LIBCPP_ALWAYS_INLINE constexpr wstring __to_wstring(_Integer __val) {
-#      if _LIBCPP_PREFERRED_OVERLOAD
+#      ifdef _LIBCPP_PREFERRED_OVERLOAD
   if !consteval {
     return std::to_wstring(__val);
   } else

>From 0cfcfd7cbe702bba0c048928a52c8c370b9f0187 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 23 Jun 2026 16:16:25 +0800
Subject: [PATCH 4/6] Address review comments

---
 libcxx/include/string                         | 108 +++++++-----------
 libcxx/src/string.cpp                         |  18 ---
 .../test/libcxx/transitive_includes/cxx03.csv |   2 +
 .../test/libcxx/transitive_includes/cxx11.csv |   2 +
 .../test/libcxx/transitive_includes/cxx14.csv |   2 +
 .../test/libcxx/transitive_includes/cxx17.csv |   2 +
 .../test/libcxx/transitive_includes/cxx20.csv |   2 +
 .../test/libcxx/transitive_includes/cxx23.csv |   4 +
 8 files changed, 58 insertions(+), 82 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 61e347c34075c..b5b74aecfb317 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -596,6 +596,8 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #  include <__algorithm/remove.h>
 #  include <__algorithm/remove_if.h>
 #  include <__assert>
+#  include <__charconv/to_chars_integral.h>
+#  include <__charconv/to_chars_result.h>
 #  include <__config>
 #  include <__cstddef/size_t.h>
 #  include <__debug_utils/sanitizers.h>
@@ -625,6 +627,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #  include <__ranges/from_range.h>
 #  include <__string/char_traits.h>
 #  include <__string/extern_template_lists.h>
+#  include <__system_error/errc.h>
 #  include <__type_traits/conditional.h>
 #  include <__type_traits/desugars_to.h>
 #  include <__type_traits/enable_if.h>
@@ -663,12 +666,6 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #    include <cwchar>
 #  endif
 
-#  if _LIBCPP_STD_VER >= 26
-#    include <__charconv/to_chars_integral.h>
-#    include <__charconv/to_chars_result.h>
-#    include <__system_error/errc.h>
-#  endif
-
 // standard-mandated includes
 
 // [iterator.range]
@@ -3780,14 +3777,14 @@ stoull(const string& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const string& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const string& __str, size_t* __idx = nullptr);
 
-#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
+#  if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD) || defined(_LIBCPP_BUILDING_LIBRARY)
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long __val);
-#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
+#  endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD) || defined(_LIBCPP_BUILDING_LIBRARY)
 
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(double __val);
@@ -3807,14 +3804,14 @@ stoull(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI double stod(const wstring& __str, size_t* __idx = nullptr);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI long double stold(const wstring& __str, size_t* __idx = nullptr);
 
-#    if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
+#    if _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD) || defined(_LIBCPP_BUILDING_LIBRARY)
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long __val);
-#    endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD)
+#    endif // _LIBCPP_STD_VER < 26 || defined(_LIBCPP_PREFERRED_OVERLOAD) || defined(_LIBCPP_BUILDING_LIBRARY)
 
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(float __val);
 [[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(double __val);
@@ -3823,11 +3820,8 @@ stoull(const wstring& __str, size_t* __idx = nullptr, int __base = 10);
 
 #  if _LIBCPP_STD_VER >= 26
 
-// Note: _LIBCPP_ALWAYS_INLINE is used for generating direct invocations to dylib versions for Clang,
-// which avoids code size regression in C++26.
-
 template <class _String, class _Integer>
-constexpr _String __integer_to_string(_Integer __val) {
+_LIBCPP_HIDE_FROM_ABI constexpr _String __integer_to_string(_Integer __val) {
   // numeric_limits::digits10 returns value less on 1 than desired for unsigned numbers.
   // For example, for 1-byte unsigned value digits10 is 2 (999 can not be represented), so we need +1 here.
   constexpr size_t __buf_size = numeric_limits<_Integer>::digits10 + 2; // +1 for minus, +1 for digits10
@@ -3837,90 +3831,76 @@ constexpr _String __integer_to_string(_Integer __val) {
   return _String(__buf, __ptr);
 }
 
-template <class _Integer>
-_LIBCPP_ALWAYS_INLINE constexpr string __to_string(_Integer __val) {
+template <class _CharT, class _Integer>
+_LIBCPP_HIDE_FROM_ABI constexpr string __to_basic_string(_Integer __val) {
 #    ifdef _LIBCPP_PREFERRED_OVERLOAD
   if !consteval {
     return std::to_string(__val);
   } else
 #    endif // _LIBCPP_PREFERRED_OVERLOAD
   {
-    return std::__integer_to_string<string>(__val);
+    return std::__integer_to_string<basic_string<_CharT>>(__val);
   }
 }
 
-#    if _LIBCPP_HAS_WIDE_CHARACTERS
-template <class _Integer>
-_LIBCPP_ALWAYS_INLINE constexpr wstring __to_wstring(_Integer __val) {
+#    if !defined(_LIBCPP_BUILDING_LIBRARY)
+
 #      ifdef _LIBCPP_PREFERRED_OVERLOAD
-  if !consteval {
-    return std::to_wstring(__val);
-  } else
+#        define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE _LIBCPP_PREFERRED_OVERLOAD
+#      else // _LIBCPP_PREFERRED_OVERLOAD
+#        define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE
 #      endif // _LIBCPP_PREFERRED_OVERLOAD
-  {
-    return std::__integer_to_string<wstring>(__val);
-  }
-}
-#    endif   // _LIBCPP_HAS_WIDE_CHARACTERS
-
-inline namespace __cpp26 {
-
-#    ifdef _LIBCPP_PREFERRED_OVERLOAD
-#      define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE _LIBCPP_PREFERRED_OVERLOAD
-#    else // _LIBCPP_PREFERRED_OVERLOAD
-#      define _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE
-#    endif // _LIBCPP_PREFERRED_OVERLOAD
 
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(int __val) {
-  return std::__to_string(__val);
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(int __val) {
+  return std::__to_basic_string<char>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(unsigned __val) {
-  return std::__to_string(__val);
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(unsigned __val) {
+  return std::__to_basic_string<char>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(long __val) {
-  return std::__to_string(__val);
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string to_string(long __val) {
+  return std::__to_basic_string<char>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
 to_string(unsigned long __val) {
-  return std::__to_string(__val);
+  return std::__to_basic_string<char>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
 to_string(long long __val) {
-  return std::__to_string(__val);
+  return std::__to_basic_string<char>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr string
 to_string(unsigned long long __val) {
-  return std::__to_string(__val);
+  return std::__to_basic_string<char>(__val);
 }
 
-#    if _LIBCPP_HAS_WIDE_CHARACTERS
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring to_wstring(int __val) {
-  return std::__to_wstring(__val);
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring to_wstring(int __val) {
+  return std::__to_basic_string<wchar_t>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
 to_wstring(unsigned __val) {
-  return std::__to_wstring(__val);
+  return std::__to_basic_string<wchar_t>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring to_wstring(long __val) {
-  return std::__to_wstring(__val);
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring to_wstring(long __val) {
+  return std::__to_basic_string<wchar_t>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
 to_wstring(unsigned long __val) {
-  return std::__to_wstring(__val);
+  return std::__to_basic_string<wchar_t>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
 to_wstring(long long __val) {
-  return std::__to_wstring(__val);
+  return std::__to_basic_string<wchar_t>(__val);
 }
-[[nodiscard]] _LIBCPP_ALWAYS_INLINE _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE constexpr wstring
 to_wstring(unsigned long long __val) {
-  return std::__to_wstring(__val);
+  return std::__to_basic_string<wchar_t>(__val);
 }
-#    endif // _LIBCPP_HAS_WIDE_CHARACTERS
+#      endif // _LIBCPP_HAS_WIDE_CHARACTERS
 
-#    undef _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE
+#      undef _LIBCPP_PREFERRED_OVERLOAD_IF_AVAILABLE
 
-} // namespace __cpp26
+#    endif // !defined(_LIBCPP_BUILDING_LIBRARY)
 
 #  endif // _LIBCPP_STD_VER >= 26
 
diff --git a/libcxx/src/string.cpp b/libcxx/src/string.cpp
index 812c4080650f0..fcbd1f2800073 100644
--- a/libcxx/src/string.cpp
+++ b/libcxx/src/string.cpp
@@ -330,24 +330,6 @@ inline wide_printf get_swprintf() {
 
 } // unnamed namespace
 
-#ifndef _LIBCPP_PREFERRED_OVERLOAD
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(int);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(long long);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI string to_string(unsigned long long);
-
-#  if _LIBCPP_HAS_WIDE_CHARACTERS
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(int);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(long long);
-[[__nodiscard__]] _LIBCPP_EXPORTED_FROM_ABI wstring to_wstring(unsigned long long);
-#  endif
-#endif
-
 string to_string(int val) { return std::__integer_to_string<string>(val); }
 string to_string(long val) { return std::__integer_to_string<string>(val); }
 string to_string(long long val) { return std::__integer_to_string<string>(val); }
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..6098fea245611 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -156,6 +156,7 @@ bitset algorithm
 bitset atomic
 bitset bit
 bitset cctype
+bitset cerrno
 bitset climits
 bitset cmath
 bitset compare
@@ -2077,6 +2078,7 @@ string algorithm
 string atomic
 string bit
 string cctype
+string cerrno
 string climits
 string cmath
 string compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..6098fea245611 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -156,6 +156,7 @@ bitset algorithm
 bitset atomic
 bitset bit
 bitset cctype
+bitset cerrno
 bitset climits
 bitset cmath
 bitset compare
@@ -2077,6 +2078,7 @@ string algorithm
 string atomic
 string bit
 string cctype
+string cerrno
 string climits
 string cmath
 string compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..690a39e09c110 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -159,6 +159,7 @@ bitset algorithm
 bitset atomic
 bitset bit
 bitset cctype
+bitset cerrno
 bitset climits
 bitset cmath
 bitset compare
@@ -2118,6 +2119,7 @@ string algorithm
 string atomic
 string bit
 string cctype
+string cerrno
 string climits
 string cmath
 string compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..a50204ecb4b9a 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -156,6 +156,7 @@ bitset algorithm
 bitset atomic
 bitset bit
 bitset cctype
+bitset cerrno
 bitset climits
 bitset cmath
 bitset compare
@@ -2130,6 +2131,7 @@ string algorithm
 string atomic
 string bit
 string cctype
+string cerrno
 string climits
 string cmath
 string compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..2e0830dac6512 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -141,6 +141,7 @@ bitset algorithm
 bitset atomic
 bitset bit
 bitset cctype
+bitset cerrno
 bitset climits
 bitset cmath
 bitset compare
@@ -2160,6 +2161,7 @@ string algorithm
 string atomic
 string bit
 string cctype
+string cerrno
 string climits
 string cmath
 string compare
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..069f1c29ff051 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -47,6 +47,7 @@ bit cstdint
 bit limits
 bit version
 bitset cctype
+bitset cerrno
 bitset climits
 bitset compare
 bitset cstdint
@@ -136,6 +137,7 @@ cinttypes cstdint
 cmath limits
 cmath version
 codecvt cctype
+codecvt cerrno
 codecvt climits
 codecvt clocale
 codecvt compare
@@ -788,6 +790,7 @@ queue typeinfo
 queue vector
 queue version
 random cctype
+random cerrno
 random climits
 random cmath
 random compare
@@ -964,6 +967,7 @@ streambuf tuple
 streambuf typeinfo
 streambuf version
 string cctype
+string cerrno
 string climits
 string compare
 string cstdint

>From d840be60ee9486dff677f0b6217337d5b9b3a02e Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 23 Jun 2026 16:16:49 +0800
Subject: [PATCH 5/6] Update synopsis comments

---
 libcxx/include/string                         | 24 +++++++++----------
 .../string.conversions/to_string.pass.cpp     | 12 +++++-----
 .../string.conversions/to_wstring.pass.cpp    | 12 +++++-----
 3 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index b5b74aecfb317..8372ca5f63294 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -540,12 +540,12 @@ float       stof (const string& str, size_t* idx = nullptr);
 double      stod (const string& str, size_t* idx = nullptr);
 long double stold(const string& str, size_t* idx = nullptr);
 
-string to_string(int val);
-string to_string(unsigned val);
-string to_string(long val);
-string to_string(unsigned long val);
-string to_string(long long val);
-string to_string(unsigned long long val);
+string to_string(int val);                      // constexpr since C++26
+string to_string(unsigned val);                 // constexpr since C++26
+string to_string(long val);                     // constexpr since C++26
+string to_string(unsigned long val);            // constexpr since C++26
+string to_string(long long val);                // constexpr since C++26
+string to_string(unsigned long long val);       // constexpr since C++26
 string to_string(float val);
 string to_string(double val);
 string to_string(long double val);
@@ -560,12 +560,12 @@ float       stof (const wstring& str, size_t* idx = nullptr);
 double      stod (const wstring& str, size_t* idx = nullptr);
 long double stold(const wstring& str, size_t* idx = nullptr);
 
-wstring to_wstring(int val);
-wstring to_wstring(unsigned val);
-wstring to_wstring(long val);
-wstring to_wstring(unsigned long val);
-wstring to_wstring(long long val);
-wstring to_wstring(unsigned long long val);
+wstring to_wstring(int val);                    // constexpr since C++26
+wstring to_wstring(unsigned val);               // constexpr since C++26
+wstring to_wstring(long val);                   // constexpr since C++26
+wstring to_wstring(unsigned long val);          // constexpr since C++26
+wstring to_wstring(long long val);              // constexpr since C++26
+wstring to_wstring(unsigned long long val);     // constexpr since C++26
 wstring to_wstring(float val);
 wstring to_wstring(double val);
 wstring to_wstring(long double val);
diff --git a/libcxx/test/std/strings/string.conversions/to_string.pass.cpp b/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
index a36d21592b08d..fb08efd17c82c 100644
--- a/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_string.pass.cpp
@@ -8,12 +8,12 @@
 
 // <string>
 
-// string to_string(int val);
-// string to_string(unsigned val);
-// string to_string(long val);
-// string to_string(unsigned long val);
-// string to_string(long long val);
-// string to_string(unsigned long long val);
+// string to_string(int val);                   // constexpr since C++26
+// string to_string(unsigned val);              // constexpr since C++26
+// string to_string(long val);                  // constexpr since C++26
+// string to_string(unsigned long val);         // constexpr since C++26
+// string to_string(long long val);             // constexpr since C++26
+// string to_string(unsigned long long val);    // constexpr since C++26
 // string to_string(float val);
 // string to_string(double val);
 // string to_string(long double val);
diff --git a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
index 22d06bffb84b8..a8f691a2ff4cd 100644
--- a/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
+++ b/libcxx/test/std/strings/string.conversions/to_wstring.pass.cpp
@@ -10,12 +10,12 @@
 
 // <string>
 
-// wstring to_wstring(int val);
-// wstring to_wstring(unsigned val);
-// wstring to_wstring(long val);
-// wstring to_wstring(unsigned long val);
-// wstring to_wstring(long long val);
-// wstring to_wstring(unsigned long long val);
+// wstring to_wstring(int val);                 // constexpr since C++26
+// wstring to_wstring(unsigned val);            // constexpr since C++26
+// wstring to_wstring(long val);                // constexpr since C++26
+// wstring to_wstring(unsigned long val);       // constexpr since C++26
+// wstring to_wstring(long long val);           // constexpr since C++26
+// wstring to_wstring(unsigned long long val);  // constexpr since C++26
 // wstring to_wstring(float val);
 // wstring to_wstring(double val);
 // wstring to_wstring(long double val);

>From 0b727e696d32f30e8790fc098fae4770d550d163 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Tue, 23 Jun 2026 16:24:15 +0800
Subject: [PATCH 6/6] Fixup `__to_basic_string`

---
 libcxx/include/string | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index 8372ca5f63294..c600ff4af80ab 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -3832,10 +3832,17 @@ _LIBCPP_HIDE_FROM_ABI constexpr _String __integer_to_string(_Integer __val) {
 }
 
 template <class _CharT, class _Integer>
-_LIBCPP_HIDE_FROM_ABI constexpr string __to_basic_string(_Integer __val) {
+_LIBCPP_HIDE_FROM_ABI constexpr basic_string<_CharT> __to_basic_string(_Integer __val) {
 #    ifdef _LIBCPP_PREFERRED_OVERLOAD
   if !consteval {
-    return std::to_string(__val);
+#      if _LIBCPP_HAS_WIDE_CHARACTERS
+    if constexpr (is_same_v<_CharT, wchar_t>) {
+      return std::to_wstring(__val);
+    } else
+#      endif // _LIBCPP_HAS_WIDE_CHARACTERS
+    {
+      return std::to_string(__val);
+    }
   } else
 #    endif // _LIBCPP_PREFERRED_OVERLOAD
   {



More information about the libcxx-commits mailing list