On Mon, 1 Jun 2026 at 17:02, Tomasz Kamiński <[email protected]> wrote:
>
> 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.
> ---
> v2:
> - changes error message and commit description
>
> OK for trunk?
>
> libstdc++-v3/include/std/format | 23 ++++++++++++++++++-
> libstdc++-v3/testsuite/std/format/debug.cc | 18 +++++++++++++--
> .../std/format/debug_nonunicode_neg.cc | 19 +++++++++++++++
> 3 files changed, 57 insertions(+), 3 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/std/format/debug_nonunicode_neg.cc
>
> diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
> index fe0f611b833..adcd3090b0b 100644
> --- a/libstdc++-v3/include/std/format
> +++ b/libstdc++-v3/include/std/format
> @@ -1116,12 +1116,33 @@ namespace __format
> }
>
> template<typename _CharT, typename _Out>
> - _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"
> + " expression");
"expressions" plural
> +#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<CharT> null(WIDEN("\0"), 1);
> + res = fdebug(null);
> + VERIFY( res == WIDEN(R"("\u{0}")") );
> + res = fdebug(null[0]);
> + VERIFY( res == WIDEN(R"('\u{0}')") );
> }
>
> template<typename CharT>
> @@ -831,6 +838,14 @@ test_all()
> {
> test_basic_escapes<char>();
> test_basic_escapes<wchar_t>();
> +
> +#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<char>();
> test_ascii_escapes<wchar_t>();
> test_extended_ascii<char>();
> @@ -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..39aecca91f0
> --- /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 <format>
> +
> +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 "debug ouput for non-unicode literal encoding supports
> only" }
Does this dg-prune-output need to be adjusted for the new
__builtin_constexpr_diag string?