diff mbox series

[committed] libstdc++: Disable std::formatter<char8_t, C> specialization

Message ID 20240922165729.1437944-1-jwakely@redhat.com
State New
Headers show
Series [committed] libstdc++: Disable std::formatter<char8_t, C> specialization | expand

Commit Message

Jonathan Wakely Sept. 22, 2024, 4:57 p.m. UTC
Tested x86_64-linux. Pushed to trunk.

-- >8 --

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.
---
 libstdc++-v3/include/std/format               | 95 ++++++++++---------
 .../std/format/formatter/requirements.cc      | 17 ++++
 2 files changed, 66 insertions(+), 46 deletions(-)
diff mbox series

Patch

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 4c5377aabec..100a53dfd76 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,53 +2185,53 @@  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;
+/// @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;
 
-      [[__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);
-      }
-
-      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); }
-
-    private:
-      __format::__formatter_int<_CharT> _M_f;
-    };
-
-#if defined __SIZEOF_INT128__ && defined __STRICT_ANSI__
-  template<typename _Tp, __format::__char _CharT>
-    requires (__is_one_of<_Tp, __int128, unsigned __int128>::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);
-      }
-
-      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); }
-
-    private:
-      __format::__formatter_int<_CharT> _M_f;
-    };
+#if defined __SIZEOF_INT128__
+  template<> inline constexpr bool __is_formattable_integer<__int128>  = true;
+  template<> inline constexpr bool __is_formattable_integer<unsigned __int128>
+      = true;
 #endif
 
+  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
+
+  /// Format an integer.
+  template<typename _Tp, __format::__char _CharT>
+    requires __format::__is_formattable_integer<_Tp>
+    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);
+      }
+
+      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); }
+
+    private:
+      __format::__formatter_int<_CharT> _M_f;
+    };
+
 #if defined __glibcxx_to_chars
   /// Format a floating-point value.
   template<__format::__formattable_float _Tp, __format::__char _CharT>
@@ -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 bde67e586ef..416b9a8ede5 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>;