From: Tomasz Kamiński Date: Fri, 15 May 2026 09:07:03 +0000 (+0200) Subject: libstdc++: Support limited escaped formatting for non-Unicode literals. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=73cac4e628d408a267fed38521e8323309bdccb4;p=thirdparty%2Fgcc.git libstdc++: Support limited escaped formatting for non-Unicode literals. This restores limited support debug (escaped) output for string when literal encoding is non-Unicode, by allowing strings that contains only printable ASCII characters and standard defined escapes (expanded with nul (\0)). This covers common use-cases, while still preserving flexibility to provide proper handling for escaping of encoding-specific characters. libstdc++-v3/ChangeLog: * include/std/format (__format::__write_escaped_ascii): Mark as _GLIBCXX_CONSTEXPR_FORMAT and at compile time, reject strings containing characters other than printable ASCII and standard escapes. * testsuite/std/format/debug.cc: Test basic_escapes and \0 at compile-time. * testsuite/std/format/debug_nonunicode_neg.cc: New test. Reviewed-by: Jonathan Wakely Signed-off-by: Tomasz Kamiński --- diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format index fe0f611b833..4a517b70889 100644 --- a/libstdc++-v3/include/std/format +++ b/libstdc++-v3/include/std/format @@ -1116,12 +1116,33 @@ namespace __format } template - _Out + _GLIBCXX_CONSTEXPR_FORMAT _Out __write_escaped_ascii(_Out __out, basic_string_view<_CharT> __str, _Term_char __term) { using _Str_view = basic_string_view<_CharT>; + if consteval { + // As set of the escaped characters depends on the encoding, for + // compile time allow only printable ASCII and standard escapes. + constexpr _Str_view __supported(_GLIBCXX_WIDEN( + "ABCDEFGHIJKLMNOPQRSTUWXYZ" + "abdeefghijklmnopqrstuwzyz" + " !#$%&'()*+-./:;<=>?[]^_{|}~" + "0123456789" "\t\n\r\\\"\'\0" + ), 95); + if (__str.find_first_not_of(__supported) != _Str_view::npos) +#if __has_builtin(__builtin_constexpr_diag) + __builtin_constexpr_diag (2, "", + "for non-Unicode literal encodings, only" + " printable ASCII characters and standard" + " escape sequencess can be escaped in constant" + " expressions"); +#else + __asm__(""); +#endif + } + auto __first = __str.begin(); auto const __last = __str.end(); while (__first != __last) diff --git a/libstdc++-v3/testsuite/std/format/debug.cc b/libstdc++-v3/testsuite/std/format/debug.cc index 01bb9074ba7..959822b2182 100644 --- a/libstdc++-v3/testsuite/std/format/debug.cc +++ b/libstdc++-v3/testsuite/std/format/debug.cc @@ -75,6 +75,13 @@ test_basic_escapes() VERIFY( res == WIDEN(R"("'")") ); res = fdebug(apos[0]); VERIFY( res == WIDEN(R"('\'')") ); + + // This is not standard escape, but still supported at compile time. + const std::basic_string null(WIDEN("\0"), 1); + res = fdebug(null); + VERIFY( res == WIDEN(R"("\u{0}")") ); + res = fdebug(null[0]); + VERIFY( res == WIDEN(R"('\u{0}')") ); } template @@ -831,6 +838,14 @@ test_all() { test_basic_escapes(); test_basic_escapes(); + +#ifndef UNICODE_ENC + // For non-unicode literal encoding debug output only supports + // printable ASCII and standard escapes at compile time + if (std::is_constant_evaluated()) + return true; +#endif + test_ascii_escapes(); test_ascii_escapes(); test_extended_ascii(); @@ -857,8 +872,7 @@ test_all() return true; } -#if defined(__glibcxx_constexpr_format) && defined(UNICODE_ENC) -// Deboug ouput is supported only for unicode literal encoding +#ifdef __glibcxx_constexpr_format static_assert(test_all()); #endif diff --git a/libstdc++-v3/testsuite/std/format/debug_nonunicode_neg.cc b/libstdc++-v3/testsuite/std/format/debug_nonunicode_neg.cc new file mode 100644 index 00000000000..cc2b11ecfd1 --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/debug_nonunicode_neg.cc @@ -0,0 +1,19 @@ +// { dg-options "-fexec-charset=ISO-8859-1" } +// { dg-do compile { target c++26 } } +// { dg-require-effective-target cxx11_abi } +// { dg-require-iconv "ISO-8859-1" } + +#include + +constexpr bool +test_format(std::string_view str) +{ + (void)std::format("{:?}", str); + return true; +} + +static_assert(test_format("\x10")); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_format("Åëÿ")); // { dg-error "in 'constexpr' expansion of" } + +// { dg-prune-output "for non-Unicode literal encodings, only printable ASCII characters and standard" } +