[clang] [Clang] add support for C23 'H', 'D', and 'DD' length modifiers (PR #201098)
Oleksandr Tarasiuk via cfe-commits
cfe-commits at lists.llvm.org
Mon Jun 8 07:02:09 PDT 2026
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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 <oleksandr.tarasiuk at outlook.com>
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}}
}
More information about the cfe-commits
mailing list