Skip to content

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

Description

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementSomething can be improvedfixedSomething works now, yay!formatC++20/23 format

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions