[clang] Reject invalid integer constants in unevaluated preprocessor operands (PR #134884)

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 8 10:43:28 PDT 2025


https://github.com/AaronBallman updated https://github.com/llvm/llvm-project/pull/134884

>From 2661b9381e0a182fb53a81c8bf66cecd51c73b0f Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Tue, 8 Apr 2025 13:17:32 -0400
Subject: [PATCH 1/2] Reject invalid integer constants in unevaluated
 preprocessor operands

Clang was previously accepting invalid code like:

  #if 1 ? 1 : 999999999999999999999
  #endif

because the integer constant (which is too large to fit into any
standard or extended integer type) was in an unevaluated branch of the
conditional operator. Similar invalid code involving || or && was also
accepted and is now rejected.

Fixes #134658
---
 clang/docs/ReleaseNotes.rst         |  8 ++++++
 clang/lib/Lex/PPExpressions.cpp     |  5 ++--
 clang/test/Preprocessor/constants.c | 40 +++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/Preprocessor/constants.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f8f4dfbafb4f8..6c86406ef36db 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -351,6 +351,14 @@ Bug Fixes in This Version
 - Defining an integer literal suffix (e.g., ``LL``) before including
   ``<stdint.h>`` in a freestanding build no longer causes invalid token pasting
   when using the ``INTn_C`` macros. (#GH85995)
+- Clang no longer accepts invalid integer constants which are too large to fit
+  into any (standard or extended) integer type when the constant is unevaluated.
+  Merely forming the token is sufficient to render the program invalid. Code
+  like this was previously accepted and is now rejected (#GH134658):
+  .. code-block:: c
+
+    #if 1 ? 1 : 999999999999999999999
+    #endif
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index b031571907441..6a6762828a20e 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -345,9 +345,8 @@ static bool EvaluateValue(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
     // Parse the integer literal into Result.
     if (Literal.GetIntegerValue(Result.Val)) {
       // Overflow parsing integer literal.
-      if (ValueLive)
-        PP.Diag(PeekTok, diag::err_integer_literal_too_large)
-            << /* Unsigned */ 1;
+      PP.Diag(PeekTok, diag::err_integer_literal_too_large)
+          << /* Unsigned */ 1;
       Result.Val.setIsUnsigned(true);
     } else {
       // Set the signedness of the result to match whether there was a U suffix
diff --git a/clang/test/Preprocessor/constants.c b/clang/test/Preprocessor/constants.c
new file mode 100644
index 0000000000000..d6241a4f1ceee
--- /dev/null
+++ b/clang/test/Preprocessor/constants.c
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -E -verify %s
+
+// C++ [lex.icon]p4 and C 6.4.4.1p2 + 6.4.4.2p7 both require C and C++ to
+// validate the integer constant value when converting a preprocessing token
+// into a token for semantic analysis, even within the preprocessor itself.
+
+// Plain integer constant.
+#if 999999999999999999999 // expected-error {{integer literal is too large to be represented in any integer type}}
+#endif
+
+// These cases were previously incorrectly accepted. See GH134658.
+
+// Integer constant in an unevaluated branch of a conditional.
+#if 1 ? 1 : 999999999999999999999 // expected-error {{integer literal is too large to be represented in any integer type}}
+#endif
+
+// Integer constant in an unevaluated operand of a logical operator.
+#if 0 && 999999999999999999999 // expected-error {{integer literal is too large to be represented in any integer type}}
+#endif
+
+#if 1 || 999999999999999999999 // expected-error {{integer literal is too large to be represented in any integer type}}
+#endif
+
+// Make sure we also catch it in an elif condition.
+#if 0
+#elif 1 || 999999999999999999999 // expected-error {{integer literal is too large to be represented in any integer type}}
+#endif
+
+// However, if the block is skipped entirely, then it doesn't matter how
+// invalid the constant value is.
+#if 0
+int x = 999999999999999999999;
+
+#if 999999999999999999999
+#endif
+
+#if 0 && 999999999999999999999
+#endif
+
+#endif

>From a4329eec3ff4dc1d8a343637428f1a67d87e24e5 Mon Sep 17 00:00:00 2001
From: Aaron Ballman <aaron at aaronballman.com>
Date: Tue, 8 Apr 2025 13:43:09 -0400
Subject: [PATCH 2/2] Fix formatting, NFC

---
 clang/lib/Lex/PPExpressions.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/lib/Lex/PPExpressions.cpp b/clang/lib/Lex/PPExpressions.cpp
index 6a6762828a20e..48835121b40e9 100644
--- a/clang/lib/Lex/PPExpressions.cpp
+++ b/clang/lib/Lex/PPExpressions.cpp
@@ -345,8 +345,7 @@ static bool EvaluateValue(PPValue &Result, Token &PeekTok, DefinedTracker &DT,
     // Parse the integer literal into Result.
     if (Literal.GetIntegerValue(Result.Val)) {
       // Overflow parsing integer literal.
-      PP.Diag(PeekTok, diag::err_integer_literal_too_large)
-          << /* Unsigned */ 1;
+      PP.Diag(PeekTok, diag::err_integer_literal_too_large) << /* Unsigned */ 1;
       Result.Val.setIsUnsigned(true);
     } else {
       // Set the signedness of the result to match whether there was a U suffix



More information about the cfe-commits mailing list