<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">