Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -97,6 +97,9 @@ ----------------------------------- - Clang constexpr evaluator now prints template arguments when displaying template-specialization function calls. +- When describing the failure of static assertion, clang prints the integer representation + of the value as well as its character representation when the user-provided expression + is of character type. Also, clang escapes the character representation if it is non-printable. Bug Fixes in This Version ------------------------- Index: clang/include/clang/Basic/Diagnostic.h =================================================================== --- clang/include/clang/Basic/Diagnostic.h +++ clang/include/clang/Basic/Diagnostic.h @@ -1841,6 +1841,10 @@ const DiagnosticOptions &Opts, bool ReportDiags = true); +/// pushEscapedString - Append Str to the diagnostic buffer, +/// escaping non-printable characters and ill-formed code unit sequences. +void pushEscapedString(StringRef Str, SmallVectorImpl &OutStr, + bool UseUCN = false); } // namespace clang #endif // LLVM_CLANG_BASIC_DIAGNOSTIC_H Index: clang/lib/Basic/Diagnostic.cpp =================================================================== --- clang/lib/Basic/Diagnostic.cpp +++ clang/lib/Basic/Diagnostic.cpp @@ -802,7 +802,8 @@ /// pushEscapedString - Append Str to the diagnostic buffer, /// escaping non-printable characters and ill-formed code unit sequences. -static void pushEscapedString(StringRef Str, SmallVectorImpl &OutStr) { +void clang::pushEscapedString(StringRef Str, SmallVectorImpl &OutStr, + bool UseUCN) { OutStr.reserve(OutStr.size() + Str.size()); auto *Begin = reinterpret_cast(Str.data()); llvm::raw_svector_ostream OutStream(OutStr); @@ -834,12 +835,27 @@ continue; } // Unprintable code point. - OutStream << ""; + if (UseUCN) + OutStream << "\\u" + << llvm::format_hex_no_prefix(CodepointValue, /*Width=*/4, + /*Upper=*/false); + else + OutStream << ""; continue; } // Invalid code unit. - OutStream << "<" << llvm::format_hex_no_prefix(*Begin, 2, true) << ">"; + if (UseUCN) + OutStream << "\\x" + << llvm::format_hex_no_prefix(*Begin, /*Width=*/2, + /*Upper=*/false); + else + OutStream << "<" + << llvm::format_hex_no_prefix(*Begin, /*Width=*/2, + /*Upper=*/true) + << ">"; ++Begin; } } Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -16817,13 +16817,31 @@ "Bool type, but value is not 0 or 1"); llvm::raw_svector_ostream OS(Str); OS << (BoolValue ? "true" : "false"); - } else if (T->isCharType()) { + } else { // Same is true for chars. - Str.push_back('\''); - Str.push_back(V.getInt().getExtValue()); - Str.push_back('\''); - } else - V.getInt().toString(Str); + // We want to print the character representation for `char` type + // and need to escape it if it is not printable. + const auto *BTy = T->getAs(); + if (BTy && (BTy->getKind() == BuiltinType::Char_S || + BTy->getKind() == BuiltinType::Char_U || + BTy->getKind() == BuiltinType::Char8)) { + llvm::raw_svector_ostream OS(Str); + int64_t CharVal = V.getInt().getExtValue(); + Str.push_back('\''); + StringRef Escaped = escapeCStyle(CharVal); + if (!Escaped.empty()) { + OS << Escaped; + } else { + const char CharArr[] = {static_cast(CharVal)}; + pushEscapedString(StringRef(CharArr, sizeof(CharArr)), Str, + /*UseUCN=*/true); + } + OS << "' ("; + V.getInt().toString(Str); + OS << ")"; + } else + V.getInt().toString(Str); + } break; Index: clang/test/Lexer/cxx1z-trigraphs.cpp =================================================================== --- clang/test/Lexer/cxx1z-trigraphs.cpp +++ clang/test/Lexer/cxx1z-trigraphs.cpp @@ -21,7 +21,7 @@ #if !ENABLED_TRIGRAPHS // expected-error@11 {{}} expected-warning@11 {{trigraph ignored}} -// expected-error@13 {{failed}} expected-warning@13 {{trigraph ignored}} expected-note@13 {{evaluates to ''?' == '#''}} +// expected-error@13 {{failed}} expected-warning@13 {{trigraph ignored}} expected-note@13 {{evaluates to ''?' (63) == '#' (35)'}} // expected-error@16 {{}} // expected-error@20 {{}} #else Index: clang/test/SemaCXX/static-assert-cxx26.cpp =================================================================== --- clang/test/SemaCXX/static-assert-cxx26.cpp +++ clang/test/SemaCXX/static-assert-cxx26.cpp @@ -289,3 +289,10 @@ Bad b; // expected-note {{in instantiation}} } + +namespace EscapeInDiagnostic { +static_assert('\u{9}' == (char)1, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\t' (9) == '\u0001' (1)'}} +static_assert((char8_t)-128 == (char8_t)-123, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\x80' (128) == '\x85' (133)'}} +} Index: clang/test/SemaCXX/static-assert.cpp =================================================================== --- clang/test/SemaCXX/static-assert.cpp +++ clang/test/SemaCXX/static-assert.cpp @@ -262,7 +262,19 @@ return 'c'; } static_assert(getChar() == 'a', ""); // expected-error {{failed}} \ - // expected-note {{evaluates to ''c' == 'a''}} + // expected-note {{evaluates to ''c' (99) == 'a' (97)'}} + static_assert((char)9 == '\x61', ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\t' (9) == 'a' (97)'}} + static_assert((char)10 == '\0', ""); // expected-error {{failed}} \ + // expected-note {{n' (10) == '\u0000' (0)'}} + // The note above is intended to match "evaluates to '\n' (10) == '\u0000' (0)'", but if we write it as it is, + // the "\n" cannot be consumed by the diagnostic consumer. + static_assert((signed char)10 == (char)-123, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to '10 == '\x85' (-123)'}} + static_assert((char)-4 == (unsigned char)-8, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\xfc' (-4) == 248'}} + static_assert((char)-128 == (char)-123, ""); // expected-error {{failed}} \ + // expected-note {{evaluates to ''\x80' (-128) == '\x85' (-123)'}} /// Bools are printed as bools. constexpr bool invert(bool b) {