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
>
>

Reply via email to