https://github.com/a-tarasyuk updated https://github.com/llvm/llvm-project/pull/201098
>From 4f186083fe8aedb3e3c1dd694afbfe56320f2c9b Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Tue, 2 Jun 2026 14:05:25 +0300 Subject: [PATCH 1/7] [Clang] add support for C23 'H', 'D', and 'DD' length modifiers --- clang/docs/ReleaseNotes.rst | 1 + clang/include/clang/AST/FormatString.h | 4 ++ clang/lib/AST/FormatString.cpp | 45 +++++++++++++++++++ clang/lib/AST/PrintfFormatString.cpp | 22 ++++++++- clang/lib/AST/ScanfFormatString.cpp | 15 +++++++ clang/test/Sema/format-strings-decimal.c | 57 ++++++++++++++++++++++++ 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 clang/test/Sema/format-strings-decimal.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index cef93e25f1e7d..ecaffff1e03f2 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -227,6 +227,7 @@ C2y Feature Support C23 Feature Support ^^^^^^^^^^^^^^^^^^^ - Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349) +- Clang now supports the C23 ``H``, ``D``, and ``DD`` length modifiers. (#GH116962) Objective-C Language Changes ----------------------------- diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index a3382e1a1d007..f7f99fcbc08cc 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -80,6 +80,9 @@ class LengthModifier { AsInt3264, // 'I' (MSVCRT, like __int3264 from MIDL) AsInt64, // 'I64' (MSVCRT, like __int64) AsLongDouble, // 'L' + AsDecimal32, // 'H' (C23, _Decimal32) + AsDecimal64, // 'D' (C23, _Decimal64) + AsDecimal128, // 'DD' (C23, _Decimal128) AsAllocate, // for '%as', GNU extension to C90 scanf AsMAllocate, // for '%ms', GNU extension to scanf AsWide, // 'w' (MSVCRT, like l but only for c, C, s, S, or Z @@ -97,6 +100,7 @@ class LengthModifier { return 1; case AsLongLong: case AsChar: + case AsDecimal128: return 2; case AsInt32: case AsInt64: diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index 7e1ac0de6dcaf..7c327ab20f4b6 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -286,6 +286,25 @@ bool clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, ++I; lmKind = LengthModifier::AsInt3264; break; + case 'H': + if (LO.C23) { + lmKind = LengthModifier::AsDecimal32; + ++I; + break; + } + return false; + case 'D': + if (LO.C23) { + ++I; + if (I != E && *I == 'D') { + ++I; + lmKind = LengthModifier::AsDecimal128; + } else { + lmKind = LengthModifier::AsDecimal64; + } + break; + } + return false; case 'w': lmKind = LengthModifier::AsWide; ++I; @@ -875,6 +894,12 @@ const char *analyze_format_string::LengthModifier::toString() const { return "I64"; case AsLongDouble: return "L"; + case AsDecimal32: + return "H"; + case AsDecimal64: + return "D"; + case AsDecimal128: + return "DD"; case AsAllocate: return "a"; case AsMAllocate: @@ -1202,6 +1227,23 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target, default: return false; } + + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: + switch (CS.getKind()) { + case ConversionSpecifier::aArg: + case ConversionSpecifier::AArg: + case ConversionSpecifier::eArg: + case ConversionSpecifier::EArg: + case ConversionSpecifier::fArg: + case ConversionSpecifier::FArg: + case ConversionSpecifier::gArg: + case ConversionSpecifier::GArg: + return LO.C23; + default: + return false; + } } llvm_unreachable("Invalid LengthModifier Kind!"); } @@ -1217,6 +1259,9 @@ bool FormatSpecifier::hasStandardLengthModifier() const { case LengthModifier::AsSizeT: case LengthModifier::AsPtrDiff: case LengthModifier::AsLongDouble: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return true; case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index 6610a2de9e083..e39c27cdf1b63 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -604,6 +604,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx, case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: case LengthModifier::AsWide: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return ArgType::Invalid(); } @@ -642,6 +645,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx, case LengthModifier::AsAllocate: case LengthModifier::AsMAllocate: case LengthModifier::AsWide: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return ArgType::Invalid(); } @@ -658,9 +664,18 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx, } } - if (LM.getKind() == LengthModifier::AsLongDouble) + switch (LM.getKind()) { + case LengthModifier::AsLongDouble: return Ctx.LongDoubleTy; - return Ctx.DoubleTy; + case LengthModifier::AsDecimal32: + return ArgType(Ctx.DoubleTy, "_Decimal32"); + case LengthModifier::AsDecimal64: + return ArgType(Ctx.DoubleTy, "_Decimal64"); + case LengthModifier::AsDecimal128: + return ArgType(Ctx.LongDoubleTy, "_Decimal128"); + default: + return Ctx.DoubleTy; + } } if (CS.getKind() == ConversionSpecifier::nArg) { @@ -692,6 +707,9 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx, case LengthModifier::AsInt3264: case LengthModifier::AsInt64: case LengthModifier::AsWide: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return ArgType::Invalid(); case LengthModifier::AsShortLong: llvm_unreachable("only used for OpenCL which doesn not handle nArg"); diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 90cbbd60bbcf5..8e4300820db74 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -309,6 +309,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsInt3264: case LengthModifier::AsWide: case LengthModifier::AsShortLong: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return ArgType::Invalid(); } llvm_unreachable("Unsupported LengthModifier Type"); @@ -353,6 +356,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsInt3264: case LengthModifier::AsWide: case LengthModifier::AsShortLong: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return ArgType::Invalid(); } llvm_unreachable("Unsupported LengthModifier Type"); @@ -373,6 +379,12 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { return ArgType::PtrTo(Ctx.DoubleTy); case LengthModifier::AsLongDouble: return ArgType::PtrTo(Ctx.LongDoubleTy); + case LengthModifier::AsDecimal32: + return ArgType::PtrTo(ArgType(Ctx.FloatTy, "_Decimal32")); + case LengthModifier::AsDecimal64: + return ArgType::PtrTo(ArgType(Ctx.DoubleTy, "_Decimal64")); + case LengthModifier::AsDecimal128: + return ArgType::PtrTo(ArgType(Ctx.LongDoubleTy, "_Decimal128")); default: return ArgType::Invalid(); } @@ -451,6 +463,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsInt3264: case LengthModifier::AsWide: case LengthModifier::AsShortLong: + case LengthModifier::AsDecimal32: + case LengthModifier::AsDecimal64: + case LengthModifier::AsDecimal128: return ArgType::Invalid(); } diff --git a/clang/test/Sema/format-strings-decimal.c b/clang/test/Sema/format-strings-decimal.c new file mode 100644 index 0000000000000..abb02255c7339 --- /dev/null +++ b/clang/test/Sema/format-strings-decimal.c @@ -0,0 +1,57 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c23 -fsyntax-only -verify -Wformat %s + +int printf(const char *restrict, ...); +int scanf(const char *restrict, ...); + +void t1(float f, double d, long double ld) { + printf("%Hf", f); + printf("%He", f); + printf("%Hg", f); + printf("%Ha", f); + printf("%HF", f); + printf("%HE", f); + printf("%HG", f); + printf("%HA", f); + + printf("%Df", d); + printf("%De", d); + printf("%Dg", d); + printf("%Da", d); + printf("%DF", d); + printf("%DE", d); + printf("%DG", d); + printf("%DA", d); + + printf("%DDf", ld); + printf("%DDe", ld); + printf("%DDg", ld); + printf("%DDa", ld); + printf("%DDF", ld); + printf("%DDE", ld); + printf("%DDG", ld); + printf("%DDA", ld); +} + +void t2(int i, float f, double d) { + printf("%Df", i); // expected-warning{{format specifies type '_Decimal64'}} + printf("%Hf", i); // expected-warning{{format specifies type '_Decimal32'}} + printf("%DDf", i); // expected-warning{{format specifies type '_Decimal128'}} +} + +void t3(float f) { + printf("%Hd", f); // expected-warning{{length modifier 'H' results in undefined behavior or no effect with 'd' conversion specifier}} + printf("%Dd", f); // expected-warning{{length modifier 'D' results in undefined behavior or no effect with 'd' conversion specifier}} + printf("%DDd", f); // expected-warning{{length modifier 'DD' results in undefined behavior or no effect with 'd' conversion specifier}} + printf("%Hs", "str"); // expected-warning{{length modifier 'H' results in undefined behavior or no effect with 's' conversion specifier}} +} + +void t4(double *d_ptr, float *f_ptr, long double *ld_ptr) { + scanf("%Hf", f_ptr); + scanf("%Df", d_ptr); + scanf("%DDf", ld_ptr); +} + +void t5(int *i_ptr) { + scanf("%Df", i_ptr); // expected-warning{{format specifies type '_Decimal64 *'}} + scanf("%Hf", i_ptr); // expected-warning{{format specifies type '_Decimal32 *'}} +} >From 71a8051b799aaf65e13c4af2518e65b774cdb8c0 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Fri, 5 Jun 2026 22:21:46 +0300 Subject: [PATCH 2/7] handle unsupported type --- clang/include/clang/AST/FormatString.h | 7 ++ .../clang/Basic/DiagnosticSemaKinds.td | 4 ++ clang/lib/AST/FormatString.cpp | 20 ++++-- clang/lib/AST/PrintfFormatString.cpp | 6 +- clang/lib/AST/ScanfFormatString.cpp | 6 +- clang/lib/Sema/SemaChecking.cpp | 23 ++++++ clang/test/Sema/format-strings-decimal.c | 70 +++++++++---------- 7 files changed, 91 insertions(+), 45 deletions(-) diff --git a/clang/include/clang/AST/FormatString.h b/clang/include/clang/AST/FormatString.h index f7f99fcbc08cc..ce05a07becb41 100644 --- a/clang/include/clang/AST/FormatString.h +++ b/clang/include/clang/AST/FormatString.h @@ -266,6 +266,7 @@ class ArgType { enum Kind { UnknownTy, InvalidTy, + UnsupportedTy, SpecificTy, ObjCPointerTy, CPointerTy, @@ -316,7 +317,13 @@ class ArgType { ArgType(CanQualType T) : K(SpecificTy), T(T) {} static ArgType Invalid() { return ArgType(InvalidTy); } + static ArgType Unsupported(const char *N) { + ArgType A(UnsupportedTy); + A.Name = N; + return A; + } bool isValid() const { return K != InvalidTy; } + bool isUnsupported() const { return K == UnsupportedTy; } bool isSizeT() const { return TK == TypeKind::SizeT; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4e3585b7b8191..5ced7fcab6aa7 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10706,6 +10706,10 @@ def warn_missing_format_string : Warning< def warn_scanf_nonzero_width : Warning< "zero field width in scanf format string is unused">, InGroup<Format>; +def warn_format_unsupported_type + : Warning<"format specifier requires type %0 which is not supported in " + "this implementation">, + InGroup<Format>; def warn_format_conversion_argument_type_mismatch : Warning< "format specifies type %0 but the argument has " "%select{type|underlying type}2 %1">, diff --git a/clang/lib/AST/FormatString.cpp b/clang/lib/AST/FormatString.cpp index 7c327ab20f4b6..000d66aee5fbe 100644 --- a/clang/lib/AST/FormatString.cpp +++ b/clang/lib/AST/FormatString.cpp @@ -434,6 +434,9 @@ ArgType::matchesType(ASTContext &C, QualType argTy) const { case InvalidTy: llvm_unreachable("ArgType must be valid"); + case UnsupportedTy: + return NoMatch; + case UnknownTy: return Match; @@ -710,6 +713,8 @@ ArgType::matchesArgType(ASTContext &C, const ArgType &Other) const { // Per matchesType. if (K == AK::InvalidTy || Other.K == AK::InvalidTy) return NoMatch; + if (K == AK::UnsupportedTy || Other.K == AK::UnsupportedTy) + return NoMatch; if (K == AK::UnknownTy || Other.K == AK::UnknownTy) return Match; @@ -800,6 +805,8 @@ QualType ArgType::getRepresentativeType(ASTContext &C) const { llvm_unreachable("No representative type for Invalid ArgType"); case UnknownTy: llvm_unreachable("No representative type for Unknown ArgType"); + case UnsupportedTy: + llvm_unreachable("No representative type for Unsupported ArgType"); case AnyCharTy: Res = C.CharTy; break; @@ -834,7 +841,10 @@ QualType ArgType::getRepresentativeType(ASTContext &C) const { } std::string ArgType::getRepresentativeTypeName(ASTContext &C) const { - std::string S = getRepresentativeType(C).getAsString(C.getPrintingPolicy()); + std::string S; + if (K != UnsupportedTy) + S = getRepresentativeType(C).getAsString(C.getPrintingPolicy()); + std::string Alias; if (Name) { // Use a specific name for this type, e.g. "size_t". @@ -848,9 +858,11 @@ std::string ArgType::getRepresentativeTypeName(ASTContext &C) const { Alias.clear(); } - if (!Alias.empty()) - return std::string("'") + Alias + "' (aka '" + S + "')"; - return std::string("'") + S + "'"; + if (Alias.empty()) + return std::string("'") + S + "'"; + + return std::string("'") + Alias + "'" + + (K == UnsupportedTy ? "" : " (aka '" + S + "')"); } //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/PrintfFormatString.cpp b/clang/lib/AST/PrintfFormatString.cpp index e39c27cdf1b63..24ba1a814eaf3 100644 --- a/clang/lib/AST/PrintfFormatString.cpp +++ b/clang/lib/AST/PrintfFormatString.cpp @@ -668,11 +668,11 @@ ArgType PrintfSpecifier::getScalarArgType(ASTContext &Ctx, case LengthModifier::AsLongDouble: return Ctx.LongDoubleTy; case LengthModifier::AsDecimal32: - return ArgType(Ctx.DoubleTy, "_Decimal32"); + return ArgType::Unsupported("_Decimal32"); case LengthModifier::AsDecimal64: - return ArgType(Ctx.DoubleTy, "_Decimal64"); + return ArgType::Unsupported("_Decimal64"); case LengthModifier::AsDecimal128: - return ArgType(Ctx.LongDoubleTy, "_Decimal128"); + return ArgType::Unsupported("_Decimal128"); default: return Ctx.DoubleTy; } diff --git a/clang/lib/AST/ScanfFormatString.cpp b/clang/lib/AST/ScanfFormatString.cpp index 8e4300820db74..4539191243b98 100644 --- a/clang/lib/AST/ScanfFormatString.cpp +++ b/clang/lib/AST/ScanfFormatString.cpp @@ -380,11 +380,11 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const { case LengthModifier::AsLongDouble: return ArgType::PtrTo(Ctx.LongDoubleTy); case LengthModifier::AsDecimal32: - return ArgType::PtrTo(ArgType(Ctx.FloatTy, "_Decimal32")); + return ArgType::PtrTo(ArgType::Unsupported("_Decimal32")); case LengthModifier::AsDecimal64: - return ArgType::PtrTo(ArgType(Ctx.DoubleTy, "_Decimal64")); + return ArgType::PtrTo(ArgType::Unsupported("_Decimal64")); case LengthModifier::AsDecimal128: - return ArgType::PtrTo(ArgType(Ctx.LongDoubleTy, "_Decimal128")); + return ArgType::PtrTo(ArgType::Unsupported("_Decimal128")); default: return ArgType::Invalid(); } diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8a8c9cc9d2c23..9ba25c396048a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -7951,6 +7951,10 @@ class CheckFormatHandler : public analyze_format_string::FormatStringHandler { const char *startSpecifier, unsigned specifierLen, unsigned argIndex); + bool CheckUnsupportedType(const analyze_format_string::ArgType &AT, + const Expr *E, const char *startSpecifier, + unsigned specifierLen); + template <typename Range> void EmitFormatDiagnostic(PartialDiagnostic PDiag, SourceLocation StringLoc, bool IsStringLocation, Range StringRange, @@ -7988,6 +7992,19 @@ void CheckFormatHandler::HandleIncompleteSpecifier(const char *startSpecifier, getSpecifierRange(startSpecifier, specifierLen)); } +bool CheckFormatHandler::CheckUnsupportedType( + const analyze_format_string::ArgType &AT, const Expr *E, + const char *startSpecifier, unsigned specifierLen) { + if (!AT.isUnsupported()) + return false; + + EmitFormatDiagnostic(S.PDiag(diag::warn_format_unsupported_type) + << AT.getRepresentativeTypeName(S.Context), + E->getExprLoc(), /*IsStringLocation=*/false, + getSpecifierRange(startSpecifier, specifierLen)); + return true; +} + void CheckFormatHandler::HandleInvalidLengthModifier( const analyze_format_string::FormatSpecifier &FS, const analyze_format_string::ConversionSpecifier &CS, @@ -9295,6 +9312,9 @@ bool CheckPrintfHandler::checkFormatExpr( return true; } + if (CheckUnsupportedType(AT, E, StartSpecifier, SpecifierLen)) + return true; + ArgType::MatchKind ImplicitMatch = ArgType::NoMatch; ArgType::MatchKind Match = AT.matchesType(S.Context, ExprTy); ArgType::MatchKind OrigMatch = Match; @@ -9796,6 +9816,9 @@ bool CheckScanfHandler::HandleScanfSpecifier( return true; } + if (CheckUnsupportedType(AT, Ex, startSpecifier, specifierLen)) + return true; + analyze_format_string::ArgType::MatchKind Match = AT.matchesType(S.Context, Ex->getType()); Match = handleFormatSignedness(Match, S.getDiagnostics(), Ex->getExprLoc()); diff --git a/clang/test/Sema/format-strings-decimal.c b/clang/test/Sema/format-strings-decimal.c index abb02255c7339..b4689ef40933a 100644 --- a/clang/test/Sema/format-strings-decimal.c +++ b/clang/test/Sema/format-strings-decimal.c @@ -4,38 +4,38 @@ int printf(const char *restrict, ...); int scanf(const char *restrict, ...); void t1(float f, double d, long double ld) { - printf("%Hf", f); - printf("%He", f); - printf("%Hg", f); - printf("%Ha", f); - printf("%HF", f); - printf("%HE", f); - printf("%HG", f); - printf("%HA", f); - - printf("%Df", d); - printf("%De", d); - printf("%Dg", d); - printf("%Da", d); - printf("%DF", d); - printf("%DE", d); - printf("%DG", d); - printf("%DA", d); - - printf("%DDf", ld); - printf("%DDe", ld); - printf("%DDg", ld); - printf("%DDa", ld); - printf("%DDF", ld); - printf("%DDE", ld); - printf("%DDG", ld); - printf("%DDA", ld); + printf("%Hf", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%He", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%Hg", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%Ha", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%HF", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%HE", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%HG", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%HA", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + + printf("%Df", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%De", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%Dg", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%Da", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%DF", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%DE", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%DG", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%DA", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + + printf("%DDf", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDe", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDg", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDa", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDF", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDE", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDG", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%DDA", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} } -void t2(int i, float f, double d) { - printf("%Df", i); // expected-warning{{format specifies type '_Decimal64'}} - printf("%Hf", i); // expected-warning{{format specifies type '_Decimal32'}} - printf("%DDf", i); // expected-warning{{format specifies type '_Decimal128'}} +void t2(int i) { + printf("%Df", i); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} + printf("%Hf", i); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} + printf("%DDf", i); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} } void t3(float f) { @@ -46,12 +46,12 @@ void t3(float f) { } void t4(double *d_ptr, float *f_ptr, long double *ld_ptr) { - scanf("%Hf", f_ptr); - scanf("%Df", d_ptr); - scanf("%DDf", ld_ptr); + scanf("%Hf", f_ptr); // expected-warning{{format specifier requires type '_Decimal32 *' which is not supported}} + scanf("%Df", d_ptr); // expected-warning{{format specifier requires type '_Decimal64 *' which is not supported}} + scanf("%DDf", ld_ptr); // expected-warning{{format specifier requires type '_Decimal128 *' which is not supported}} } void t5(int *i_ptr) { - scanf("%Df", i_ptr); // expected-warning{{format specifies type '_Decimal64 *'}} - scanf("%Hf", i_ptr); // expected-warning{{format specifies type '_Decimal32 *'}} + scanf("%Df", i_ptr); // expected-warning{{format specifier requires type '_Decimal64 *' which is not supported}} + scanf("%Hf", i_ptr); // expected-warning{{format specifier requires type '_Decimal32 *' which is not supported}} } >From 6e33c2cd5ea886d83b700499133ad45d2f33e127 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Fri, 5 Jun 2026 22:25:43 +0300 Subject: [PATCH 3/7] fix formatting --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 5ced7fcab6aa7..6034ea7132588 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10706,10 +10706,9 @@ def warn_missing_format_string : Warning< def warn_scanf_nonzero_width : Warning< "zero field width in scanf format string is unused">, InGroup<Format>; -def warn_format_unsupported_type - : Warning<"format specifier requires type %0 which is not supported in " - "this implementation">, - InGroup<Format>; +def warn_format_unsupported_type : Warning< + "format specifier requires type %0 which is not supported in this implementation">, + InGroup<Format>; def warn_format_conversion_argument_type_mismatch : Warning< "format specifies type %0 but the argument has " "%select{type|underlying type}2 %1">, >From 135a9a8da34e655f3379c9a4b5bbf6581447b2d0 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 8 Jun 2026 16:46:26 +0300 Subject: [PATCH 4/7] update release note --- clang/docs/ReleaseNotes.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 29fae6340cedc..6e9f35d10234f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -236,7 +236,9 @@ C23 Feature Support ^^^^^^^^^^^^^^^^^^^ - Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349) - Clang now supports the C23 ``wN`` and ``wfN`` length modifiers. (#GH116962) -- Clang now supports the C23 ``H``, ``D``, and ``DD`` length modifiers. (#GH116962) +- Clang now recognizes the C23 ``H``, ``D``, and ``DD`` length modifiers in + format strings and diagnoses their use because Clang does not yet support + the corresponding decimal floating-point types, ``_Decimal32``, ``_Decimal64``, and ``_Decimal128``. (#GH116962) Objective-C Language Changes ----------------------------- >From 46372c93666d987db81d68853273755f92bd6adf Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 8 Jun 2026 16:46:48 +0300 Subject: [PATCH 5/7] update names to follow code style --- clang/lib/Sema/SemaChecking.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index fa8d22336e4f0..deab5cc6c0938 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -7994,14 +7994,14 @@ void CheckFormatHandler::HandleIncompleteSpecifier(const char *startSpecifier, bool CheckFormatHandler::CheckUnsupportedType( const analyze_format_string::ArgType &AT, const Expr *E, - const char *startSpecifier, unsigned specifierLen) { + const char *StartSpecifier, unsigned SpecifierLen) { if (!AT.isUnsupported()) return false; EmitFormatDiagnostic(S.PDiag(diag::warn_format_unsupported_type) << AT.getRepresentativeTypeName(S.Context), E->getExprLoc(), /*IsStringLocation=*/false, - getSpecifierRange(startSpecifier, specifierLen)); + getSpecifierRange(StartSpecifier, SpecifierLen)); return true; } >From c03e4b84bdba72014f58044c9b8083a8d4639254 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 8 Jun 2026 16:47:05 +0300 Subject: [PATCH 6/7] update diagnostic message --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6034ea7132588..c7ddc589e9249 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10707,7 +10707,7 @@ def warn_scanf_nonzero_width : Warning< "zero field width in scanf format string is unused">, InGroup<Format>; def warn_format_unsupported_type : Warning< - "format specifier requires type %0 which is not supported in this implementation">, + "format specifies type %0 which is not supported yet">, InGroup<Format>; def warn_format_conversion_argument_type_mismatch : Warning< "format specifies type %0 but the argument has " >From c69a01c94f90da280aa2721080d4f5453e3a1770 Mon Sep 17 00:00:00 2001 From: Oleksandr Tarasiuk <[email protected]> Date: Mon, 8 Jun 2026 17:01:48 +0300 Subject: [PATCH 7/7] update tests --- clang/test/Sema/format-strings-decimal.c | 68 ++++++++++++------------ 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/clang/test/Sema/format-strings-decimal.c b/clang/test/Sema/format-strings-decimal.c index b4689ef40933a..121a0d6d758b3 100644 --- a/clang/test/Sema/format-strings-decimal.c +++ b/clang/test/Sema/format-strings-decimal.c @@ -4,38 +4,38 @@ int printf(const char *restrict, ...); int scanf(const char *restrict, ...); void t1(float f, double d, long double ld) { - printf("%Hf", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%He", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%Hg", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%Ha", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%HF", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%HE", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%HG", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%HA", f); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - - printf("%Df", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%De", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%Dg", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%Da", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%DF", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%DE", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%DG", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%DA", d); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - - printf("%DDf", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDe", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDg", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDa", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDF", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDE", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDG", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} - printf("%DDA", ld); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%Hf", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%He", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%Hg", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%Ha", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%HF", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%HE", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%HG", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%HA", f); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + + printf("%Df", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%De", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%Dg", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%Da", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%DF", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%DE", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%DG", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%DA", d); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + + printf("%DDf", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDe", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDg", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDa", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDF", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDE", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDG", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} + printf("%DDA", ld); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} } void t2(int i) { - printf("%Df", i); // expected-warning{{format specifier requires type '_Decimal64' which is not supported}} - printf("%Hf", i); // expected-warning{{format specifier requires type '_Decimal32' which is not supported}} - printf("%DDf", i); // expected-warning{{format specifier requires type '_Decimal128' which is not supported}} + printf("%Df", i); // expected-warning{{format specifies type '_Decimal64' which is not supported yet}} + printf("%Hf", i); // expected-warning{{format specifies type '_Decimal32' which is not supported yet}} + printf("%DDf", i); // expected-warning{{format specifies type '_Decimal128' which is not supported yet}} } void t3(float f) { @@ -46,12 +46,12 @@ void t3(float f) { } void t4(double *d_ptr, float *f_ptr, long double *ld_ptr) { - scanf("%Hf", f_ptr); // expected-warning{{format specifier requires type '_Decimal32 *' which is not supported}} - scanf("%Df", d_ptr); // expected-warning{{format specifier requires type '_Decimal64 *' which is not supported}} - scanf("%DDf", ld_ptr); // expected-warning{{format specifier requires type '_Decimal128 *' which is not supported}} + scanf("%Hf", f_ptr); // expected-warning{{format specifies type '_Decimal32 *' which is not supported yet}} + scanf("%Df", d_ptr); // expected-warning{{format specifies type '_Decimal64 *' which is not supported yet}} + scanf("%DDf", ld_ptr); // expected-warning{{format specifies type '_Decimal128 *' which is not supported yet}} } void t5(int *i_ptr) { - scanf("%Df", i_ptr); // expected-warning{{format specifier requires type '_Decimal64 *' which is not supported}} - scanf("%Hf", i_ptr); // expected-warning{{format specifier requires type '_Decimal32 *' which is not supported}} + scanf("%Df", i_ptr); // expected-warning{{format specifies type '_Decimal64 *' which is not supported yet}} + scanf("%Hf", i_ptr); // expected-warning{{format specifies type '_Decimal32 *' which is not supported yet}} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
