https://gcc.gnu.org/g:0f52a92ab249bde64b7570d4cf549437a3283520
commit r15-3776-g0f52a92ab249bde64b7570d4cf549437a3283520 Author: Jonathan Wakely <jwak...@redhat.com> Date: Fri Sep 20 17:26:35 2024 +0100 libstdc++: Disable std::formatter<char8_t, C> specialization I noticed that char8_t was missing from the list of types that were prevented from using the std::formatter partial specialization for integer types. That partial specialization was also matching cv-qualified integer types, because std::integral<const int> is true. This change simplifies the constraints by introducing a new variable template which is only true for cv-unqualified integer types, with explicit specializations to exclude the character types. This should be slightly more efficient than the previous constraints that checked std::integral<T> and (!__is_one_of<T, char, wchar_t, ...>). It also avoids the need for a separate std::formatter specialization for 128-bit integers, as they can be handled by the new variable template too. libstdc++-v3/ChangeLog: * include/std/format (__format::__is_formattable_integer): New variable template and specializations. (template<integral, __char> struct formatter): Replace constraints on first arg with __is_formattable_integer. * testsuite/std/format/formatter/requirements.cc: Check that std::formatter specializations for char8_t and const int are disabled. Diff: --- libstdc++-v3/include/std/format | 53 ++++++++++++---------- .../testsuite/std/format/formatter/requirements.cc | 17 +++++++ 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index 4c5377aabec6..100a53dfd76f 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -1468,8 +1468,9 @@ namespace __format // We can format a floating-point type iff it is usable with to_chars. template<typename _Tp> - concept __formattable_float = requires (_Tp __t, char* __p) - { __format::to_chars(__p, __p, __t, chars_format::scientific, 6); }; + concept __formattable_float + = is_same_v<remove_cv_t<_Tp>, _Tp> && requires (_Tp __t, char* __p) + { __format::to_chars(__p, __p, __t, chars_format::scientific, 6); }; template<__char _CharT> struct __formatter_fp @@ -2184,32 +2185,33 @@ namespace __format #endif // USE_WCHAR_T /// @} - /// Format an integer. - template<integral _Tp, __format::__char _CharT> - requires (!__is_one_of<_Tp, char, wchar_t, char16_t, char32_t>::value) - struct formatter<_Tp, _CharT> - { - formatter() = default; - - [[__gnu__::__always_inline__]] - constexpr typename basic_format_parse_context<_CharT>::iterator - parse(basic_format_parse_context<_CharT>& __pc) - { - return _M_f.template _M_parse<_Tp>(__pc); - } +/// @cond undocumented +namespace __format +{ + // each cv-unqualified arithmetic type ArithmeticT other than + // char, wchar_t, char8_t, char16_t, or char32_t + template<typename _Tp> + constexpr bool __is_formattable_integer = __is_integer<_Tp>::__value; - template<typename _Out> - typename basic_format_context<_Out, _CharT>::iterator - format(_Tp __u, basic_format_context<_Out, _CharT>& __fc) const - { return _M_f.format(__u, __fc); } +#if defined __SIZEOF_INT128__ + template<> inline constexpr bool __is_formattable_integer<__int128> = true; + template<> inline constexpr bool __is_formattable_integer<unsigned __int128> + = true; +#endif - private: - __format::__formatter_int<_CharT> _M_f; - }; + template<> inline constexpr bool __is_formattable_integer<char> = false; + template<> inline constexpr bool __is_formattable_integer<wchar_t> = false; +#ifdef _GLIBCXX_USE_CHAR8_T + template<> inline constexpr bool __is_formattable_integer<char8_t> = false; +#endif + template<> inline constexpr bool __is_formattable_integer<char16_t> = false; + template<> inline constexpr bool __is_formattable_integer<char32_t> = false; +} +/// ~endcond -#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__ + /// Format an integer. template<typename _Tp, __format::__char _CharT> - requires (__is_one_of<_Tp, __int128, unsigned __int128>::value) + requires __format::__is_formattable_integer<_Tp> struct formatter<_Tp, _CharT> { formatter() = default; @@ -2229,7 +2231,6 @@ namespace __format private: __format::__formatter_int<_CharT> _M_f; }; -#endif #if defined __glibcxx_to_chars /// Format a floating-point value. @@ -2614,6 +2615,8 @@ namespace __format } // namespace __format /// @endcond +// Concept std::formattable was introduced by P2286R8 "Formatting Ranges", +// but we can't guard it with __cpp_lib_format_ranges until we define that! #if __cplusplus > 202002L // [format.formattable], concept formattable template<typename _Tp, typename _CharT> diff --git a/libstdc++-v3/testsuite/std/format/formatter/requirements.cc b/libstdc++-v3/testsuite/std/format/formatter/requirements.cc index bde67e586efc..416b9a8ede52 100644 --- a/libstdc++-v3/testsuite/std/format/formatter/requirements.cc +++ b/libstdc++-v3/testsuite/std/format/formatter/requirements.cc @@ -26,6 +26,16 @@ test_specializations() // [format.formatter.spec] static_assert( std::is_copy_assignable_v<Fi> ); static_assert( std::is_move_assignable_v<Fi> ); +#ifdef _GLIBCXX_USE_CHAR8_T + // std::string s0 = std::format("{}", u8'a'); // error: disabled formatter + using Fc8 = std::format_context::formatter_type<char8_t>; + static_assert( ! std::is_default_constructible_v<Fc8> ); + static_assert( ! std::is_copy_constructible_v<Fc8> ); + static_assert( ! std::is_move_constructible_v<Fc8> ); + static_assert( ! std::is_copy_assignable_v<Fc8> ); + static_assert( ! std::is_move_assignable_v<Fc8> ); +#endif + // std::string s1 = std::format("{}", L"foo"); // error: disabled formatter using Fw = std::format_context::formatter_type<wchar_t>; static_assert( ! std::is_default_constructible_v<Fw> ); @@ -34,6 +44,13 @@ test_specializations() // [format.formatter.spec] static_assert( ! std::is_copy_assignable_v<Fw> ); static_assert( ! std::is_move_assignable_v<Fw> ); + using Fic = std::format_context::formatter_type<const int>; // disabled + static_assert( ! std::is_default_constructible_v<Fic> ); + static_assert( ! std::is_copy_constructible_v<Fic> ); + static_assert( ! std::is_move_constructible_v<Fic> ); + static_assert( ! std::is_copy_assignable_v<Fic> ); + static_assert( ! std::is_move_assignable_v<Fic> ); + std::string s2 = std::format("{}", red); // OK, user-provided formatter VERIFY( s2 == "red" ); using Fc = std::format_context::formatter_type<color>;