[gcc r15-9828] libstdc++: Do not specialize std::formatter for incomplete type [PR120625]

Jonathan Wakely redi@gcc.gnu.org
Thu Jun 12 12:59:52 GMT 2025


https://gcc.gnu.org/g:bbe9ec92138b1dfc7c61a3d8db7351ee57586388

commit r15-9828-gbbe9ec92138b1dfc7c61a3d8db7351ee57586388
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Jun 11 11:11:52 2025 +0100

    libstdc++: Do not specialize std::formatter for incomplete type [PR120625]
    
    Using an incomplete type as the template argument for std::formatter
    specializations causes problems for program-defined specializations of
    std::formatter which have constraints. When the compiler has to find
    which specialization of std::formatter to use for the incomplete type it
    considers the program-defined specializations and checks to see if their
    constraints are satisfied, which can give errors if the constraints
    cannot be checked for incomplete types.
    
    This replaces the base class of the disabled specializations with a
    concrete class __formatter_disabled, so there is no need to match a
    specialization and no more incomplete type.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/120625
            * include/std/format (__format::__disabled): Remove.
            (__formatter_disabled): New type.
            (formatter<char*, wchar_t>, formatter<const char*, wchar_t>)
            (formatter<char[N], wchar_t>, formatter<string, wchar_t>)
            (formatter<string_view, wchar_t>): Use __formatter_disabled as
            base class instead of formatter<__disabled, wchar_t>.
            * testsuite/std/format/formatter/120625.cc: New test.
    
    Reviewed-by: Tomasz Kamiński <tkaminsk@redhat.com>
    (cherry picked from commit 76bf78d32c683af3bf88f4aef595048edbd82372)

Diff:
---
 libstdc++-v3/include/std/format                       | 18 +++++++++++-------
 libstdc++-v3/testsuite/std/format/formatter/120625.cc | 19 +++++++++++++++++++
 2 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index d25df1224ffc..7d6b75228d18 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -3017,24 +3017,28 @@ namespace __format
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
   // 3944. Formatters converting sequences of char to sequences of wchar_t
 
-  namespace __format { struct __disabled; }
+  struct __formatter_disabled
+  {
+    __formatter_disabled() = delete; // Cannot format char sequence to wchar_t
+    __formatter_disabled(const __formatter_disabled&) = delete;
+    __formatter_disabled& operator=(const __formatter_disabled&) = delete;
+  };
 
-  // std::formatter<__disabled, C> uses the primary template, which is disabled.
   template<>
     struct formatter<char*, wchar_t>
-    : private formatter<__format::__disabled, wchar_t> { };
+    : private __formatter_disabled { };
   template<>
     struct formatter<const char*, wchar_t>
-    : private formatter<__format::__disabled, wchar_t> { };
+    : private __formatter_disabled { };
   template<size_t _Nm>
     struct formatter<char[_Nm], wchar_t>
-    : private formatter<__format::__disabled, wchar_t> { };
+    : private __formatter_disabled { };
   template<class _Traits, class _Allocator>
     struct formatter<basic_string<char, _Traits, _Allocator>, wchar_t>
-    : private formatter<__format::__disabled, wchar_t> { };
+    : private __formatter_disabled { };
   template<class _Traits>
     struct formatter<basic_string_view<char, _Traits>, wchar_t>
-    : private formatter<__format::__disabled, wchar_t> { };
+    : private __formatter_disabled { };
 #endif
 
   /// An iterator after the last character written, and the number of
diff --git a/libstdc++-v3/testsuite/std/format/formatter/120625.cc b/libstdc++-v3/testsuite/std/format/formatter/120625.cc
new file mode 100644
index 000000000000..6b03af93b1c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/std/format/formatter/120625.cc
@@ -0,0 +1,19 @@
+// { dg-do compile { target c++20 } }
+
+// Bug libstdc++/120625
+// std::formatter<__disabled> specializations cause errors in user code
+
+#include <format>
+
+enum X { };
+
+// A concept that cannot be used with incomplete types:
+template<typename T>
+concept is_X = !std::is_empty_v<T> && std::is_same_v<X, T>;
+
+// A valid program-defined specialization:
+template<typename T, typename C> requires is_X<T>
+struct std::formatter<T, C> : std::formatter<int, C> { };
+
+// Instantiate the program-defined formatter specialization:
+auto s = sizeof(std::formatter<X, char>);


More information about the Libstdc++-cvs mailing list