Hi Ivan, The patch was just merged for the GCC-17. Thank you for your contributions.
On Mon, Jun 1, 2026 at 5:16 PM Jonathan Wakely <[email protected]> wrote: > On Mon, 1 Jun 2026 at 16:10, Tomasz Kamiński <[email protected]> wrote: > > > > From: Ivan Lazaric <[email protected]> > > > > In C++26 paper P3391, "constexpr formatting", has been adopted, > > part of which marks std::to_string & std::to_wstring for integers > > as constexpr. The __cpp_lib_constexpr_string FTM value is updated > > per resolution of LWG4531, "Should there be a feature-test macro > > update for constexpr std::to_(w)string?". > > > > Since pre-cxx11 copy-on-write string is not constexpr-enabled, > > restricting this constexpr-ification to cxx11 ABI strings. > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/version.def (constexpr_string): Bump to 202511. > > * include/bits/versin.h: Regenerate. > > * include/bits/basic_string.h (std::to_string, std::to_wstring) > > [__glibcxx_constexpr_string >= 202511L]: Mark as constexpr. > > * > testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc: > > New test. > > * > testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc: > > New test. > > * testsuite/21_strings/basic_string/cons/char/constexpr.cc: > Update > > __cpp_lib_constexpr_string check. > > testsuite/21_strings/basic_string/cons/wchar_t/constexpr.c: > Likewise. > > * 21_strings/basic_string/version.cc: Add check for value of > > __cpp_lib_constexpr_string in C++26. > > > > Co-authored-by: Tomasz Kamiński <[email protected]> > > --- > > v3 introduces _GLIBCXX_TO_STRING_CONSTEXPR as suggested by > > Nathan. > > OK, thanks > > > > > > libstdc++-v3/include/bits/basic_string.h | 31 +++++---- > > libstdc++-v3/include/bits/version.def | 8 +++ > > libstdc++-v3/include/bits/version.h | 7 +- > > .../basic_string/cons/char/constexpr.cc | 4 +- > > .../basic_string/cons/wchar_t/constexpr.cc | 4 +- > > .../char/to_string_constexpr.cc | 64 +++++++++++++++++++ > > .../wchar_t/to_wstring_constexpr.cc | 64 +++++++++++++++++++ > > .../21_strings/basic_string/version.cc | 12 ++++ > > 8 files changed, 177 insertions(+), 17 deletions(-) > > create mode 100644 > libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc > > create mode 100644 > libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc > > > > diff --git a/libstdc++-v3/include/bits/basic_string.h > b/libstdc++-v3/include/bits/basic_string.h > > index 23e6979f03d..65d92ebfbcf 100644 > > --- a/libstdc++-v3/include/bits/basic_string.h > > +++ b/libstdc++-v3/include/bits/basic_string.h > > @@ -4607,11 +4607,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > { return std::stod(__str, __idx); } > > #endif > > > > +#if __glibcxx_constexpr_string >= 202511L > > +# define _GLIBCXX_TO_STRING_CONSTEXPR constexpr > > +#else > > +# define _GLIBCXX_TO_STRING_CONSTEXPR inline > > +#endif > > + > > // _GLIBCXX_RESOLVE_LIB_DEFECTS > > // DR 1261. Insufficient overloads for to_string / to_wstring > > > > _GLIBCXX_NODISCARD > > - inline string > > + _GLIBCXX_TO_STRING_CONSTEXPR string > > to_string(int __val) > > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32 > > noexcept // any 32-bit value fits in the SSO buffer > > @@ -4630,7 +4636,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > } > > > > _GLIBCXX_NODISCARD > > - inline string > > + _GLIBCXX_TO_STRING_CONSTEXPR string > > to_string(unsigned __val) > > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_INT__) <= 32 > > noexcept // any 32-bit value fits in the SSO buffer > > @@ -4646,7 +4652,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > } > > > > _GLIBCXX_NODISCARD > > - inline string > > + _GLIBCXX_TO_STRING_CONSTEXPR string > > to_string(long __val) > > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32 > > noexcept // any 32-bit value fits in the SSO buffer > > @@ -4665,7 +4671,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > } > > > > _GLIBCXX_NODISCARD > > - inline string > > + _GLIBCXX_TO_STRING_CONSTEXPR string > > to_string(unsigned long __val) > > #if _GLIBCXX_USE_CXX11_ABI && (__CHAR_BIT__ * __SIZEOF_LONG__) <= 32 > > noexcept // any 32-bit value fits in the SSO buffer > > @@ -4681,7 +4687,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > } > > > > _GLIBCXX_NODISCARD > > - inline string > > + _GLIBCXX_TO_STRING_CONSTEXPR string > > to_string(long long __val) > > { > > const bool __neg = __val < 0; > > @@ -4698,7 +4704,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > } > > > > _GLIBCXX_NODISCARD > > - inline string > > + _GLIBCXX_TO_STRING_CONSTEXPR string > > to_string(unsigned long long __val) > > { > > const auto __len = __detail::__to_chars_len(__val); > > @@ -4922,32 +4928,32 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > #pragma GCC diagnostic pop > > > > _GLIBCXX_NODISCARD > > - inline wstring > > + _GLIBCXX_TO_STRING_CONSTEXPR wstring > > to_wstring(int __val) > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > > > _GLIBCXX_NODISCARD > > - inline wstring > > + _GLIBCXX_TO_STRING_CONSTEXPR wstring > > to_wstring(unsigned __val) > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > > > _GLIBCXX_NODISCARD > > - inline wstring > > + _GLIBCXX_TO_STRING_CONSTEXPR wstring > > to_wstring(long __val) > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > > > _GLIBCXX_NODISCARD > > - inline wstring > > + _GLIBCXX_TO_STRING_CONSTEXPR wstring > > to_wstring(unsigned long __val) > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > > > _GLIBCXX_NODISCARD > > - inline wstring > > + _GLIBCXX_TO_STRING_CONSTEXPR wstring > > to_wstring(long long __val) > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > > > _GLIBCXX_NODISCARD > > - inline wstring > > + _GLIBCXX_TO_STRING_CONSTEXPR wstring > > to_wstring(unsigned long long __val) > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > > > @@ -4968,6 +4974,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 > > { return std::__to_wstring_numeric(std::to_string(__val)); } > > #endif > > #endif // _GLIBCXX_USE_WCHAR_T > > +#undef _GLIBCXX_TO_STRING_CONSTEXPR > > > > _GLIBCXX_END_NAMESPACE_CXX11 > > _GLIBCXX_END_NAMESPACE_VERSION > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > > index 97f7d686519..ea8560778e5 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -1398,6 +1398,14 @@ ftms = { > > > > ftms = { > > name = constexpr_string; > > + // 202511 LWG4531 Should there be a feature-test macro update for > constexpr std::to_(w)string? > > + // P3391R2 constexpr std::format > > + values = { > > + v = 202511; > > + cxxmin = 26; > > + hosted = yes; > > + cxx11abi = yes; > > + }; > > values = { > > v = 201907; > > cxxmin = 20; > > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > > index 33eecbfb83b..59d5d9217d7 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -1542,7 +1542,12 @@ > > #undef __glibcxx_want_constexpr_flat_set > > > > #if !defined(__cpp_lib_constexpr_string) > > -# if (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI && > _GLIBCXX_HOSTED && (defined(__glibcxx_is_constant_evaluated)) > > +# if (__cplusplus > 202302L) && _GLIBCXX_USE_CXX11_ABI && > _GLIBCXX_HOSTED > > +# define __glibcxx_constexpr_string 202511L > > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_constexpr_string) > > +# define __cpp_lib_constexpr_string 202511L > > +# endif > > +# elif (__cplusplus >= 202002L) && _GLIBCXX_USE_CXX11_ABI && > _GLIBCXX_HOSTED && (defined(__glibcxx_is_constant_evaluated)) > > # define __glibcxx_constexpr_string 201907L > > # if defined(__glibcxx_want_all) || > defined(__glibcxx_want_constexpr_string) > > # define __cpp_lib_constexpr_string 201907L > > diff --git > a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc > b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc > > index 7822c89497b..34abfe60bc5 100644 > > --- > a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc > > +++ > b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/constexpr.cc > > @@ -6,8 +6,8 @@ > > > > #ifndef __cpp_lib_constexpr_string > > # error "Feature-test macro for constexpr std::string missing in > <string>" > > -#elif __cpp_lib_constexpr_string != 201907L > > -# error "Feature-test macro for constexpr std::string has wrong value > in <string>" > > +#elif __cpp_lib_constexpr_string < 201907L > > +# error "Feature-test macro for constexpr std::string has too small > value in <string>" > > #endif > > > > #include <testsuite_hooks.h> > > diff --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 > > index 44c8391ebc2..59d5ee270cc 100644 > > --- > 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 > > @@ -6,8 +6,8 @@ > > > > #ifndef __cpp_lib_constexpr_string > > # error "Feature-test macro for constexpr std::string missing in > <string>" > > -#elif __cpp_lib_constexpr_string != 201907L > > -# error "Feature-test macro for constexpr std::string has wrong value > in <string>" > > +#elif __cpp_lib_constexpr_string < 201907L > > +# error "Feature-test macro for constexpr std::string too small value > in <string>" > > #endif > > > > #include <testsuite_hooks.h> > > diff --git > a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc > b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc > > new file mode 100644 > > index 00000000000..1b5a616df54 > > --- /dev/null > > +++ > b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/to_string_constexpr.cc > > @@ -0,0 +1,64 @@ > > +// { dg-do compile { target c++26 } } > > +// { dg-require-effective-target cxx11_abi } > > + > > +#include <string> > > +#include <type_traits> > > +#include <testsuite_hooks.h> > > + > > +template<typename T> > > +constexpr void > > +test() > > +{ > > + using namespace std; > > + string res; > > + T value; > > + > > + value = 0; > > + res = to_string(value); > > + VERIFY( res == "0" ); > > + > > + value = 1; > > + res = to_string(value); > > + VERIFY( res == "1" ); > > + > > + value = 10; > > + res = to_string(value); > > + VERIFY( res == "10" ); > > + > > + value = 3000; > > + res = to_string(value); > > + VERIFY( res == "3000" ); > > + > > + value = 32767; > > + res = to_string(value); > > + VERIFY( res == "32767" ); > > + > > + if (is_unsigned_v<T>) > > + return; > > + > > + value = -1; > > + res = to_string(value); > > + VERIFY( res == "-1" ); > > + > > + value = -40; > > + res = to_string(value); > > + VERIFY( res == "-40" ); > > + > > + value = -32768; > > + res = to_string(value); > > + VERIFY( res == "-32768" ); > > +} > > + > > +constexpr bool > > +test_all() > > +{ > > + test<int>(); > > + test<unsigned int>(); > > + test<long>(); > > + test<unsigned long>(); > > + test<long long>(); > > + test<unsigned long long>(); > > + return true; > > +} > > + > > +static_assert(test_all()); > > diff --git > a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc > b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc > > new file mode 100644 > > index 00000000000..f884fc61ff5 > > --- /dev/null > > +++ > b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/to_wstring_constexpr.cc > > @@ -0,0 +1,64 @@ > > +// { dg-do compile { target c++26 } } > > +// { dg-require-effective-target cxx11_abi } > > + > > +#include <string> > > +#include <type_traits> > > +#include <testsuite_hooks.h> > > + > > +template<typename T> > > +constexpr void > > +test() > > +{ > > + using namespace std; > > + wstring res; > > + T value; > > + > > + value = 0; > > + res = to_wstring(value); > > + VERIFY( res == L"0" ); > > + > > + value = 1; > > + res = to_wstring(value); > > + VERIFY( res == L"1" ); > > + > > + value = 10; > > + res = to_wstring(value); > > + VERIFY( res == L"10" ); > > + > > + value = 3000; > > + res = to_wstring(value); > > + VERIFY( res == L"3000" ); > > + > > + value = 32767; > > + res = to_wstring(value); > > + VERIFY( res == L"32767" ); > > + > > + if (is_unsigned_v<T>) > > + return; > > + > > + value = -1; > > + res = to_wstring(value); > > + VERIFY( res == L"-1" ); > > + > > + value = -40; > > + res = to_wstring(value); > > + VERIFY( res == L"-40" ); > > + > > + value = -32768; > > + res = to_wstring(value); > > + VERIFY( res == L"-32768" ); > > +} > > + > > +constexpr bool > > +test_all() > > +{ > > + test<int>(); > > + test<unsigned int>(); > > + test<long>(); > > + test<unsigned long>(); > > + test<long long>(); > > + test<unsigned long long>(); > > + return true; > > +} > > + > > +static_assert(test_all()); > > diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/version.cc > b/libstdc++-v3/testsuite/21_strings/basic_string/version.cc > > index 71dd4dfb67f..b0cdf04274e 100644 > > --- a/libstdc++-v3/testsuite/21_strings/basic_string/version.cc > > +++ b/libstdc++-v3/testsuite/21_strings/basic_string/version.cc > > @@ -25,3 +25,15 @@ > > # endif > > # endif > > #endif > > + > > +#if __cplusplus > 202302L > > +# if _GLIBCXX_USE_CXX11_ABI > > +# if __cpp_lib_constexpr_string != 202511L > > +# error "Feature-test macro for constexpr std::string has wrong value > for C++20 in <version>" > > +# endif > > +# else // COW strings > > +# if __cpp_lib_constexpr_string != 201811L > > +# error "Feature-test macro for constexpr std::string has wrong value > for C++20 in <version>" > > +# endif > > +# endif > > +#endif > > -- > > 2.54.0 > > > >
