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

Reply via email to