fmtlib / fmt

A modern formatting library
https://fmt.dev
Other
20.86k stars 2.51k forks source link

fmt::format_to + FMT_STRING with char16_t/char32_t characters fails to compile #4198

Closed XZiar closed 1 month ago

XZiar commented 1 month ago

It's failing when using char16_t or char32_t with FMT_STRING in fmt 11.x, but it was working in fmt 10.x.

    // Using a char32_t buffer fails to compile:
    std::u32string u32buf;
    fmt::format_to(std::back_inserter(u32buf), FMT_STRING(U"{}"), 2);

Similar to #3925, PR #3931 only fixed wchar_t.

godbolt repro: https://godbolt.org/z/q4vqbTEc5

godbolt success for fmt 10.x: https://godbolt.org/z/sT6aKjfjj

vitaut commented 1 month ago

Thanks for reporting but compile-time checks are only supported for char and wchar_t strings and there are no current plans to implement them for other code unit types. We could make legacy FMT_STRING be a noop for these but it's probably better to just remove them from your code.

puetzk commented 1 week ago

We also hit this while upgrading from 10.2.1 to 11.x.

We try pretty hard to use only char{8,16}_t in our data structures, avoiding things with unspecified/non-portable encoding like std::string and std::wstring, since we build in really interesting environments like winelib (where wchar_t might be libstdc++'s ucs-4, but there's still lots of windows WCHAR running around that is utf-16 like wchar_t is on MSVC.

compile-time checks are only supported for char and wchar_t strings and there are no current plans to implement them for other code unit types

Interesting. I do see that even where it compiled in fmt 10.x the compile-time check was not actually performed. I.e. a totally incorrect usage like

#include <fmt/format.h>
#include <fmt/xchar.h>

int main() {
    (void)fmt::format(FMT_STRING(u8"hello {:x}"),u8"world");
}

compiles just fine in fmt 10.2.x, but crashes at runtime with

terminate called after throwing an instance of 'fmt::v10::format_error' what(): invalid format specifier

whereas changing it to wchar_t detects the problem at compile time

int main() {
    (void)fmt::format(FMT_STRING(L"hello {:x}"),L"world");
}

/opt/compiler-explorer/libs/fmt/10.2.1/include/fmt/core.h:2340:27: error: call to non-'constexpr' function 'void fmt::v10::detail::throw_format_error(const char*)' throw_format_error("invalid format specifier");

Interestingly, these did work with char16_t when we first introduced the use of FMT_STRING back with fmt 7.1.2

So, just to make the change somewhat more searchable:

https://godbolt.org/z/38hfK9o4r