diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-09-20 17:26:35 +0100 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2024-09-22 17:52:36 +0100 |
commit | 0f52a92ab249bde64b7570d4cf549437a3283520 (patch) | |
tree | b128beac89a80ad8424237c8e7d90aa0a87443a5 | |
parent | 83c6fe130a00c6c28cfffcc787a0a719966adfaf (diff) | |
download | gcc-0f52a92ab249bde64b7570d4cf549437a3283520.zip gcc-0f52a92ab249bde64b7570d4cf549437a3283520.tar.gz gcc-0f52a92ab249bde64b7570d4cf549437a3283520.tar.bz2 |
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.
-rw-r--r-- | libstdc++-v3/include/std/format | 53 | ||||
-rw-r--r-- | libstdc++-v3/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 4c5377a..100a53d 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 bde67e5..416b9a8 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>; |