llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Oleksandr Tarasiuk (a-tarasyuk)

<details>
<summary>Changes</summary>

This patch adds `-Wformat` support for the C23 `H`, `D`, and `DD` length 
modifiers in `printf`/`scanf` format strings. #<!-- -->116962 

---
Full diff: https://github.com/llvm/llvm-project/pull/201098.diff


6 Files Affected:

- (modified) clang/docs/ReleaseNotes.rst (+1) 
- (modified) clang/include/clang/AST/FormatString.h (+4) 
- (modified) clang/lib/AST/FormatString.cpp (+45) 
- (modified) clang/lib/AST/PrintfFormatString.cpp (+20-2) 
- (modified) clang/lib/AST/ScanfFormatString.cpp (+15) 
- (added) clang/test/Sema/format-strings-decimal.c (+57) 


``````````diff
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 
*'}}
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/201098
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to