Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

<format>: Hideous compiler errors when formatter<UDT>::format() isn't const #4202

Closed
StephanTLavavej opened this issue Nov 16, 2023 · 3 comments · Fixed by #4461
Closed

<format>: Hideous compiler errors when formatter<UDT>::format() isn't const #4202

StephanTLavavej opened this issue Nov 16, 2023 · 3 comments · Fixed by #4461
Labels
enhancement Something can be improved fixed Something works now, yay! format C++20/23 format

Comments

@StephanTLavavej
Copy link
Member

Consider this repro from DevCom-10515914 where a user-defined formatter<UDT> forgot to mark format() as const. After #3745, we reject that as required by the Standard, but the compiler error is horrible:

C:\GitHub\STL\out\x64>type meow.cpp
#include <format>
#include <iostream>

struct MyStruct {
    uint32_t value;
    bool trueFalse;
};

enum class CustomFormat { Type1, Type2 };

namespace std {

    template <>
    struct formatter<MyStruct> {
        constexpr auto parse(format_parse_context& ctx) {
            auto it = begin(ctx);

            if (it != end(ctx)) {
                switch (*it) {
                case 'A':
                    m_format = CustomFormat::Type1;
                    break;
                case 'B':
                    m_format = CustomFormat::Type2;
                    break;
                default:
                    // don't advance the iterator for unknown format specifiers
                    // return early if we don't recognize the character
                    return it;
                }

                it++;
            }

            return it;
        }

        template <typename FormatContext>
        auto format(const MyStruct& mc, FormatContext& ctx)
#ifdef MARK_FORMAT_AS_CONST
            const
#endif
        {
            auto&& out = ctx.out();
            *out++     = '*';
            *out++     = '*';
            switch (m_format) {
            case CustomFormat::Type1:
                *out++ = 'A';
                break;
            case CustomFormat::Type2:
                *out++ = 'B';
                break;
            }
            out    = format_to(out, " - {} {}", mc.value, mc.trueFalse);
            *out++ = '*';
            return out;
        }

        CustomFormat m_format{CustomFormat::Type1};
    };
} // namespace std

int main() {
    MyStruct mystruct{959, false};

    std::cout << std::format("MyStruct: {}", mystruct) << "\n";
    std::cout << std::format("MyStruct: {:A}", mystruct) << "\n";
    std::cout << std::format("MyStruct: {:B}", mystruct) << "\n";

    std::cout << "Hello World!\n";
}
C:\GitHub\STL\out\x64>cl /EHsc /nologo /W4 /std:c++latest /MTd /Od /DMARK_FORMAT_AS_CONST meow.cpp && meow
meow.cpp
MyStruct: **A - 959 false*
MyStruct: **A - 959 false*
MyStruct: **B - 959 false*
Hello World!
C:\GitHub\STL\out\x64>cl /EHsc /nologo /W4 /std:c++latest /MTd /Od meow.cpp && meow
meow.cpp
C:\GitHub\STL\out\x64\out\inc\format(687): error C2672: 'std::_Format_arg_traits<_Context>::_Type_eraser': no matching overloaded function found
        with
        [
            _Context=std::format_context
        ]
C:\GitHub\STL\out\x64\out\inc\format(684): note: could be 'auto std::_Format_arg_traits<_Context>::_Type_eraser(void)'
        with
        [
            _Context=std::format_context
        ]
C:\GitHub\STL\out\x64\out\inc\format(687): note: the associated constraints are not satisfied
C:\GitHub\STL\out\x64\out\inc\format(847): note: the concept 'std::_Formattable_with<MyStruct,std::format_context,std::formatter<MyStruct,char>>' evaluated to false
C:\GitHub\STL\out\x64\out\inc\format(665): note: 'auto std::formatter<MyStruct,char>::format<_Context>(const MyStruct &,FormatContext &)': cannot convert 'this' pointer from 'const std::formatter<MyStruct,char>' to 'std::formatter<MyStruct,char> &'
        with
        [
            _Context=std::format_context,
            FormatContext=std::format_context
        ]
C:\GitHub\STL\out\x64\out\inc\format(665): note: Conversion loses qualifiers
C:\GitHub\STL\out\x64\out\inc\format(665): note: while trying to match the argument list '(MyStruct, _Context)'
        with
        [
            _Context=std::format_context
        ]
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating constexpr function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating constexpr function 'std::_Parse_replacement_field'
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating constexpr function 'std::_Parse_format_string'
C:\GitHub\STL\out\x64\out\inc\format(687): note: the template instantiation context (the oldest one first) is
meow.cpp(67): note: see reference to function template instantiation 'std::basic_format_string<char,MyStruct &>::basic_format_string<char[13]>(const _Ty (&))' being compiled
        with
        [
            _Ty=char [13]
        ]
C:\GitHub\STL\out\x64\out\inc\format(3805): note: see reference to class template instantiation 'std::__p2286::_Format_checker<_CharT,MyStruct>' being compiled
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(3579): note: while compiling class template member function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_Format_checker(std::basic_string_view<char,std::char_traits<char>>) noexcept'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(3580): note: see reference to function template instantiation 'std::_String_view_iterator<_Traits> std::__p2286::_Compile_time_parse_format_specs<MyStruct,std::basic_format_parse_context<char>>(_ParseContext &)' being compiled
        with
        [
            _Traits=std::char_traits<char>,
            _ParseContext=std::basic_format_parse_context<char>
        ]
C:\GitHub\STL\out\x64\out\inc\format(3559): note: see reference to alias template instantiation 'std::_Format_arg_traits<_Context>::_Storage_type<MyStruct>' being compiled
        with
        [
            _Context=std::format_context
        ]
C:\GitHub\STL\out\x64\out\inc\format(3563): error C2993: 'unknown-type': is not a valid type for non-type template parameter '_Test'
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating constexpr function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating constexpr function 'std::_Parse_replacement_field'
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating constexpr function 'std::_Parse_format_string'
C:\GitHub\STL\out\x64\out\inc\format(3565): error C2641: cannot deduce template arguments for 'std::formatter'
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating constexpr function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating constexpr function 'std::_Parse_replacement_field'
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating constexpr function 'std::_Parse_format_string'
C:\GitHub\STL\out\x64\out\inc\format(3565): error C2783: 'std::formatter<_Ty,_CharT> std::formatter(void)': could not deduce template argument for '_Ty'
C:\GitHub\STL\out\x64\out\inc\format(3651): note: see declaration of 'std::formatter'
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating constexpr function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating constexpr function 'std::_Parse_replacement_field'
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating constexpr function 'std::_Parse_format_string'
C:\GitHub\STL\out\x64\out\inc\format(3565): error C2780: 'std::formatter<_Ty,_CharT> std::formatter(std::formatter<_Ty,_CharT>)': expects 1 arguments - 0 provided
C:\GitHub\STL\out\x64\out\inc\format(3650): note: see declaration of 'std::formatter'
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating constexpr function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating constexpr function 'std::_Parse_replacement_field'
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating constexpr function 'std::_Parse_format_string'
C:\GitHub\STL\out\x64\out\inc\format(3566): error C2039: 'parse': is not a member of 'std::formatter'
C:\GitHub\STL\out\x64\out\inc\format(3650): note: see declaration of 'std::formatter'
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating constexpr function 'std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating constexpr function 'std::_Parse_replacement_field'
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating constexpr function 'std::_Parse_format_string'
meow.cpp(67): error C3615: consteval function 'std::__p2286::_Compile_time_parse_format_specs' cannot result in a constant expression
C:\GitHub\STL\out\x64\out\inc\format(3555): note: failure was caused by control reaching the end of a consteval function
meow.cpp(67): note: the call stack of the evaluation (the oldest call first) is
meow.cpp(67): note: while evaluating function 'std::basic_format_string<char,MyStruct &>::basic_format_string<char[13]>(const _Ty (&))'
        with
        [
            _Ty=char [13]
        ]
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating function 'void std::_Parse_format_string<char,std::__p2286::_Format_checker<_CharT,MyStruct>>(std::basic_string_view<char,std::char_traits<char>>,_HandlerT &&)'
        with
        [
            _CharT=char,
            _HandlerT=std::__p2286::_Format_checker<char,MyStruct>
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating function 'const _CharT *std::_Parse_replacement_field<char,_HandlerT&>(const _CharT *,const _CharT *,std::__p2286::_Format_checker<_CharT,MyStruct>&)'
        with
        [
            _CharT=char,
            _HandlerT=std::__p2286::_Format_checker<char,MyStruct>
        ]
C:\GitHub\STL\out\x64\out\inc\format(1458): note: while evaluating function 'void std::__p2286::_Format_checker<_CharT,MyStruct>::_On_replacement_field(const size_t,const _CharT *) const'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(3584): note: while evaluating function 'std::_String_view_iterator<_Traits> std::__p2286::_Compile_time_parse_format_specs<MyStruct,std::basic_format_parse_context<char>>(_ParseContext &)'
        with
        [
            _Traits=std::char_traits<char>,
            _ParseContext=std::basic_format_parse_context<char>
        ]
meow.cpp(68): error C3615: consteval function 'std::__p2286::_Compile_time_parse_format_specs' cannot result in a constant expression
C:\GitHub\STL\out\x64\out\inc\format(3555): note: failure was caused by control reaching the end of a consteval function
meow.cpp(68): note: the call stack of the evaluation (the oldest call first) is
meow.cpp(68): note: while evaluating function 'std::basic_format_string<char,MyStruct &>::basic_format_string<char[15]>(const _Ty (&))'
        with
        [
            _Ty=char [15]
        ]
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating function 'void std::_Parse_format_string<char,std::__p2286::_Format_checker<_CharT,MyStruct>>(std::basic_string_view<char,std::char_traits<char>>,_HandlerT &&)'
        with
        [
            _CharT=char,
            _HandlerT=std::__p2286::_Format_checker<char,MyStruct>
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating function 'const _CharT *std::_Parse_replacement_field<char,_HandlerT&>(const _CharT *,const _CharT *,std::__p2286::_Format_checker<_CharT,MyStruct>&)'
        with
        [
            _CharT=char,
            _HandlerT=std::__p2286::_Format_checker<char,MyStruct>
        ]
C:\GitHub\STL\out\x64\out\inc\format(1473): note: while evaluating function 'const _CharT *std::__p2286::_Format_checker<_CharT,MyStruct>::_On_format_specs(const size_t,const _CharT *,const _CharT *)'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(3589): note: while evaluating function 'std::_String_view_iterator<_Traits> std::__p2286::_Compile_time_parse_format_specs<MyStruct,std::basic_format_parse_context<char>>(_ParseContext &)'
        with
        [
            _Traits=std::char_traits<char>,
            _ParseContext=std::basic_format_parse_context<char>
        ]
meow.cpp(69): error C3615: consteval function 'std::__p2286::_Compile_time_parse_format_specs' cannot result in a constant expression
C:\GitHub\STL\out\x64\out\inc\format(3555): note: failure was caused by control reaching the end of a consteval function
meow.cpp(69): note: the call stack of the evaluation (the oldest call first) is
meow.cpp(69): note: while evaluating function 'std::basic_format_string<char,MyStruct &>::basic_format_string<char[15]>(const _Ty (&))'
        with
        [
            _Ty=char [15]
        ]
C:\GitHub\STL\out\x64\out\inc\format(3805): note: while evaluating function 'void std::_Parse_format_string<char,std::__p2286::_Format_checker<_CharT,MyStruct>>(std::basic_string_view<char,std::char_traits<char>>,_HandlerT &&)'
        with
        [
            _CharT=char,
            _HandlerT=std::__p2286::_Format_checker<char,MyStruct>
        ]
C:\GitHub\STL\out\x64\out\inc\format(1524): note: while evaluating function 'const _CharT *std::_Parse_replacement_field<char,_HandlerT&>(const _CharT *,const _CharT *,std::__p2286::_Format_checker<_CharT,MyStruct>&)'
        with
        [
            _CharT=char,
            _HandlerT=std::__p2286::_Format_checker<char,MyStruct>
        ]
C:\GitHub\STL\out\x64\out\inc\format(1473): note: while evaluating function 'const _CharT *std::__p2286::_Format_checker<_CharT,MyStruct>::_On_format_specs(const size_t,const _CharT *,const _CharT *)'
        with
        [
            _CharT=char
        ]
C:\GitHub\STL\out\x64\out\inc\format(3589): note: while evaluating function 'std::_String_view_iterator<_Traits> std::__p2286::_Compile_time_parse_format_specs<MyStruct,std::basic_format_parse_context<char>>(_ParseContext &)'
        with
        [
            _Traits=std::char_traits<char>,
            _ParseContext=std::basic_format_parse_context<char>
        ]

This issue exists to track the possibility of improving this diagnostic without distorting the library code too much (in a way that risks damaging correctness).

@StephanTLavavej StephanTLavavej added enhancement Something can be improved format C++20/23 format labels Nov 16, 2023
@frederick-vs-ja
Copy link
Contributor

The associated constraints of _Type_eraser are being removed by #4133. I think we can investigate the errors after merging that PR.

@cpplearner
Copy link
Contributor

With #4133 merged, now the message is much shorter

Microsoft (R) C/C++ Optimizing Compiler Version 19.39.33218 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

test-format.cpp
stl/inc\format(3690): error C2338: static_assert failed: 'Cannot format an argument. To make type T formattable, provide a formatter<T> specialization. See N4950 [format.arg.store]/2 and [formatter.requirements].'
stl/inc\format(3690): note: the template instantiation context (the oldest one first) is
D:\test\test-format.cpp(67): note: see reference to function template instantiation 'std::string std::format<MyStruct&>(const std::basic_format_string<char,MyStruct &>,MyStruct &)' being compiled
stl/inc\format(3823): note: see reference to function template instantiation 'auto std::make_format_args<std::format_context,MyStruct&>(MyStruct &)' being compiled

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented Mar 2, 2024

The remaining issue (without my mistake in and accidental fix) should only be about the const which is made required by LWG-3636.

It seems that we can reform _Formattable_with like the following. IIUC such implementation strategy is conforming due to implicit expression variations:

template <class _Ty, class _Context, class _Formatter = _Context::template formatter_type<remove_const_t<_Ty>>>
concept _Formattable_with_non_const = semiregular<_Formatter>
                                   && requires(_Formatter& __f, _Ty&& __t, _Context __fc,
                                       basic_format_parse_context<typename _Context::char_type> __pc) {
                                          { __f.parse(__pc) } -> same_as<typename decltype(__pc)::iterator>;
                                          { __f.format(__t, __fc) } -> same_as<typename _Context::iterator>;
                                      };

template <class _Ty, class _Context, class _Formatter = _Context::template formatter_type<remove_const_t<_Ty>>>
concept _Formattable_with = _Formattable_with_non_const<_Ty, _Context, _Formatter>
                         && requires(const _Formatter& __cf, _Ty&& __t, _Context __fc) {
                                { __cf.format(__t, __fc) } -> same_as<typename _Context::iterator>;
                            };

And then split static_assert's in make_format_args/make_wformat_args.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Something can be improved fixed Something works now, yay! format C++20/23 format
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants