Open
Description
https://godbolt.org/z/vac7s4rKn
#include <cassert>
#include <format>
#include <string>
using namespace std;
int main() {
// Everyone accepts:
assert(format("{0:#}", 77) == "77");
assert(format("{0:#d}", 77) == "77");
assert(format("{0:#b}", 77) == "0b1001101");
assert(format("{0:#B}", 77) == "0B1001101");
assert(format("{0:#o}", 77) == "0115");
assert(format("{0:#x}", 77) == "0x4d");
assert(format("{0:#X}", 77) == "0X4D");
assert(format("{0:c}", 77) == "M");
// libstdc++ 14.2 accepts, libc++ 18.1 and microsoft/STL 17.10 reject:
assert(format("{0:#c}", 77) == "M");
// 77 is an int, which is an arithmetic type other than charT and bool.
// 'c' is an integer presentation type.
// Therefore, I conclude that formatting 77 with "{0:#c}" is doubly valid.
// Here's the Standardese:
// N4988 [format.string.std]/7:
// "The # option causes the alternate form to be used for the conversion.
// This option is valid for arithmetic types other than charT and bool
// or when an integer presentation type is specified, and not otherwise."
// N4988 [format.string.std]/21:
// "The available integer presentation types for integral types other than bool and charT
// are specified in Table 72."
// Table 72 - Meaning of type options for integer types [tab:format.type.int]
// Type | Meaning
// -----|--------
// b | to_chars(first, last, value, 2); the base prefix is 0b.
// B | The same as b, except that the base prefix is 0B.
// c | Copies the character static_cast<charT>(value) to the output.
// | Throws format_error if value is not in the range of representable values for charT.
// [...]
}
<source>:18:19: error: call to consteval function 'std::basic_format_string<char, int>::basic_format_string<char[7]>' is not a constant expression
18 | assert(format("{0:#c}", 77) == "M");
| ^
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/parser_std_format_spec.h:462:9: note: non-constexpr function '__throw_format_error' cannot be used in a constant expression
462 | std::__throw_format_error("The format specifier does not allow the alternate form option");
| ^
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/parser_std_format_spec.h:898:3: note: in call to '__parser.__validate({0, 0, 0, 0, 1, 1, 0, 0, 1}, &"an integer"[0], 4294967295)'
898 | __parser.__validate(__format_spec::__fields_bool, __id);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/parser_std_format_spec.h:905:3: note: in call to '__process_display_type_bool_string<char>(__formatter.__formatter_integer::__parser_, &"an integer"[0])'
905 | __format_spec::__process_display_type_bool_string(__parser, __id);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/parser_std_format_spec.h:964:5: note: in call to '__process_display_type_char<char>(__formatter.__formatter_integer::__parser_, &"an integer"[0])'
964 | __format_spec::__process_display_type_char(__parser, __id);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/formatter_integer.h:39:5: note: in call to '__process_parsed_integer<char>(__formatter.__formatter_integer::__parser_, &"an integer"[0])'
39 | __format_spec::__process_parsed_integer(__parser_, "an integer");
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/format_functions.h:183:26: note: in call to '__formatter.parse<std::basic_format_parse_context<char>>(basic_format_parse_context<char>{this->__str_, sizeof...(_Args)})'
183 | __parse_ctx.advance_to(__formatter.parse(__parse_ctx));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/format_functions.h:206:12: note: in call to '__compile_time_validate_argument<char, int, false>(basic_format_parse_context<char>{this->__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)})'
206 | return __format::__compile_time_validate_argument<_CharT, int>(__parse_ctx, __ctx);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/format_functions.h:275:7: note: in call to '__compile_time_visit_format_arg<char>(basic_format_parse_context<char>{this->__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)}, 3)'
275 | __format::__compile_time_visit_format_arg(__parse_ctx, __ctx, __type);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/format_functions.h:316:20: note: in call to '__handle_replacement_field<const char *, std::basic_format_parse_context<char>, std::__format::__compile_time_basic_format_context<char>>(&"{0:#c}"[1], &"{0:#c}"[6], basic_format_parse_context<char>{this->__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)})'
316 | __begin = __format::__handle_replacement_field(__begin, __end, __parse_ctx, __ctx);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/format_functions.h:371:5: note: in call to '__vformat_to<std::basic_format_parse_context<char>, std::__format::__compile_time_basic_format_context<char>>(basic_format_parse_context<char>{this->__str_, sizeof...(_Args)}, _Context{__types_.data(), __handles_.data(), sizeof...(_Args)})'
371 | __format::__vformat_to(basic_format_parse_context<_CharT>{__str_, sizeof...(_Args)},
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
372 | _Context{__types_.data(), __handles_.data(), sizeof...(_Args)});
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:18:19: note: in call to 'basic_format_string<char[7]>("{0:#c}")'
18 | assert(format("{0:#c}", 77) == "M");
| ^~~~~~~~
/usr/include/assert.h:93:27: note: expanded from macro 'assert'
93 | (static_cast <bool> (expr) \
| ^~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__format/format_error.h:38:52: note: declared here
38 | _LIBCPP_NORETURN inline _LIBCPP_HIDE_FROM_ABI void __throw_format_error(const char* __s) {
| ^
1 error generated.