On Thu, Jun 12, 2025 at 12:04 AM Jonathan Wakely <jwak...@redhat.com> wrote:
> 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. > --- > > Tested x86_64-linux. > LGTM. Thanks. > > This is needed for trunk and gcc-15. > > The diagnostic for trying to use those disabled formatters will now > show: > > 3119 | __formatter_disabled() = delete; // Cannot format char > sequence to wchar_t > > which seems fine. > > libstdc++-v3/include/std/format | 18 +++++++++++------- > .../testsuite/std/format/formatter/120625.cc | 19 +++++++++++++++++++ > 2 files changed, 30 insertions(+), 7 deletions(-) > create mode 100644 libstdc++-v3/testsuite/std/format/formatter/120625.cc > > diff --git a/libstdc++-v3/include/std/format > b/libstdc++-v3/include/std/format > index ec76ab0682e0..04fb23eb1367 100644 > --- a/libstdc++-v3/include/std/format > +++ b/libstdc++-v3/include/std/format > @@ -3114,24 +3114,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>); > -- > 2.49.0 > >