STL
STL copied to clipboard
`<format>`: Hideous compiler errors when `formatter<UDT>::format()` isn't `const`
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).
The associated constraints of _Type_eraser are being removed by #4133. I think we can investigate the errors after merging that PR.
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
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.