<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/106136>106136</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[libc++] `format("{0:#c}", 77)` should be accepted
</td>
</tr>
<tr>
<th>Labels</th>
<td>
libc++
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
StephanTLavavej
</td>
</tr>
</table>
<pre>
https://godbolt.org/z/vac7s4rKn
```cpp
#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.
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzUWltv27gS_jXMCxFBoizLesiDLzG2OG130eYA-yZQ0tjiQiYFknbcfTi__WCoi-920k3arRE4skgOZ75vZjikxI0RSwnwQKIJiWZ3fG1LpR--WqhLLp8-8g3fwF93mSq-PZTW1oaEY8LmhM2XqshUZT2ll4TN_yZsvuF5bAb6P5L4M-KP2--h3_zldd3eYaGQebUugJJwmnNjQFsSPp5rXSi94pcajdVCLvvGtRFySSVfgal5DtTYgoSTfV2EtHTFhSRsRFhCSdy2UkppYxR93ID-piRQnudQW2du06fVk41anVAGI_HEd4iEJJ7hbzalceyEhzMSzihhDH8zwpJeGZzuhrTibcVll8X5WeD7QeAHx1JviJxcETn5PpHqisggiF5v9vaKwO2geL3AP68I_HMwe429-WVRn47knDhpJTJji5ywCWETGgw81rsrm2Jr3zTyAsplQVci18qohSVs_vXpIw1iL_Cphr8gtzsXvw3AP9E6jqkwlEsqpEUBz6XIy_YW18KWK7Aip_ZbDVTZEjS1JZc0L7l-ckZkSlXeiVjC4pywPdmwBE1rDQak5VYo6USeDnwqQcNCaUBlPtBctZnFltzSxnyLGSWO6bOwJT0HBM5aqHVWfaMbXomim6Wd4jdA6bGhtgT61XJZcF2AgR7yE6U-D5LRiJJo0ijgNTnOw1wWzQibxzu2evvZUwmUsJCq2pmb87WBZk5eWdCSW3AGUatoBnRtoMDfrkeu5Aa0EUqeQ0iYTqgwjYVu4BFd5jJfJzKVps8lyKtc4WSmhlwsBBTIDkqTyjbTPAsDHpLxfQiy4MDhD1HkGy4qnlVwWTnjEHDNmlen9qPVTmEHxMlEXMPONiokfXLTxeyKSV0Xek8_AZfolGrRBopjZ08nVMOpRKKJ5RkJxy0OLggw9KLZ6QQoisTTTvxJh3v8kHh6335OOmTuOp5Sq1I03GAKEdq4SK9483_Dq7WLtjZROAfMuAFEeSG2yLufnfrhpBOOFBm-AsoNzVAQbDHtNSF7TtjkVFjeCZuqWrRhghrz3IKmBpnO0xw1DqcNheEjYaNW9wRjCIeota3X9lQ83emq1bNpE0kKWiNDiwYD1A39WUgnS3O5BKRUQ-duSLjr2lDrFDmT-6KJ53k9o5iTDouus5VY-zOcGrXWOaB94TgY4VdCwjF1uuJFzqsK7c2VNBY2vKKLtcxdJGBWw_pqTMJxxo3I09bQrihz2CFF6HJuhus9o0mMdiDWcQcPb2bm0lLYIjKYpnoQgpHD-W1XrR1_JOqLzrmqceHM1aoWFeh72NaV0qDxXsXl8h7XWc8nbJ5hcTn3PMLmbaGKfZrVGCvkgLB5mnZazmuuDejU2KIHpYbcK0k4HgwZCceOD6ksNP_lfUPFttYHVKSpRW9L950Nccy5RBy7nC_kdUgHQ9ZDip-e4fPyR23SbO72WU3TQkFLYFWp53MLUZO2_q3YjxIMhnAfeyH7aHB4N6O9NHWLIreAcKDDsSk9-gr6r_176JFTStiQMLZbCxGRaOK7lWpKBywZJMOYJRHCxOIOplHSuP55NdJ9ezr-FgKqwqRuSWY4UhQXof_fd31-JmGJH90kTKscjEkLYeqKf0txOXRwHOUhl-y7Wa3Dtr9OO5ZaUFuNbhG5T13iRy11Z0i6qSNq1kz6TiT-S-gcDkg4jl5NZ0Pgj-FxOHglj816-AMIfHfmTnB0pIXJSzlzABQ7En4IYTRMbhN2pNmOrXaqo3n2qXubsPtB3KVd8WAcdcEIUycbXiNvx4tDxB2_nav_XGuaK2lha_e4dfS-qGc8saUw9yR8TDHvOeiN-BvUAmtdNkrHemkQ-_iI4mAU7i-LaW63Hi82XOaQWnXkXY0VPcPY2Un7nmj8OawxHwkL2DXWWlVSK1bQ1wgp18v1CqQ9qtLZlC54ZeC9uJrSdNpKiCepy4om9QpueXMg26TEksuiOm14kQcwf-gI02DXWvax3oX5TTTSqdvw7XYtR_6B-rWO8i6L7k93qRgTePxyjxJG2M5HuD4son4VB5rS8MiL4sMa7bz7HJt-yVXwAjV9h9XiJ3pKGAxdCrrmKg0RqYa64jlgiDXbEKQfN6HuVIMSNnb0vGo12R9wlaYDaRdWpaaEOD0uiCZBuxG73GPY9vhVnH3fz8NgeLDdT9MMlkJSSsLZifNfJBP93o1rFAJZNBc30ubbZsyfFARxcKvm3bQDrXp1xfSmPv5L-mccnM3De5jeMKxdz51pLzHpXZf0I2elYXx42nbm8z4In5Yub2LQ5QPl88HxssPgCye5B1u79iT4nU6BD41k87XRB0mjmbY5uHD7qIP6CbY1lwUUdKHViq54rp3xna47I5Jw5w2EjfYeRFASTt3BXfiILbCttVM_uuCvPyoZumPgJhEi19HBTqSAvOIaClqCht0mvD24_PhhMv3jj_Tz718en_775TMVshIS-vu_fZg9pvMvv39Kx5MPdKNEQS8cQe8KCcLGNE3NyYsdRyfKQfN4gy5BguYWuke2_dORu-IhLJIw4XfwEMRsEPtR4LO78oEHkAwjf1QkeZHlwyiKEz9JEj8MfR4H_vBOPDCfDfwRGzJ_EPnMgwBGLAoWi6wYjFhYkIEPKy4qr6o2K0_p5Z0wZg0PgT8MwuFdxTOojHsTh7HdU3xXaMzu9AOOus_WS0MGfiWMNTs5VtjKvcOzNyyaUTL0XxYLZOhTU6p1VdCse_cFiru1ro7f-hG2XGderlaEzXH69t99rZV7mYDNnU2GsHlr1uaB_T8AAP__d0tFEQ">