[clang] [clang][Sema] Fix for enums overflowing (#24667) (PR #78742)

via cfe-commits cfe-commits at lists.llvm.org
Sun Feb 25 06:57:11 PST 2024


https://github.com/wheatman updated https://github.com/llvm/llvm-project/pull/78742

>From 5a3d785ef4eaa83d8944dedbb4cb1b79914fdacc Mon Sep 17 00:00:00 2001
From: Brian Wheatman <bwheatman at gmail.com>
Date: Fri, 19 Jan 2024 11:13:33 -0500
Subject: [PATCH] [clang][Sema] Fix for overflow in enumerators(#24667)

Enums which do not have a specified type can only grow to bigger types
which contain more bits than the prior types.  This means that the
largest signed integer type cannot grow to the largest unsigned integer types.

In the Process also implements N3029 Improved Normal Enumerations and N3030
Enhancements to Enumerations which brings C enums more inline with C++
enums.

Fixes #24667
---
 clang/docs/ReleaseNotes.rst |  8 +++++
 clang/lib/Sema/SemaDecl.cpp | 65 ++++++++++++++++++++++++++++---------
 clang/test/Sema/enum.c      | 43 +++++++++++++++++++++---
 clang/test/SemaCXX/enum.cpp |  2 ++
 4 files changed, 97 insertions(+), 21 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 529dd783ab73825..33a0c2d7d8bb3a4 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -135,6 +135,12 @@ C23 Feature Support
   ``__TYPE_FMTb__`` (e.g., ``__UINT_FAST64_FMTB__``) in C23 mode for use with
   macros typically exposed from ``<inttypes.h>``, such as ``PRIb8``.
   (`#81896: <https://github.com/llvm/llvm-project/issues/81896>`_).
+- Enumerations should allow values greater than INT_MAX and smaller than
+  INT_MIN, in order to provide a value-preserved set of integer constants. `N3029 Improved Normal Enumerations <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3029.htm>`_
+
+- Enumerations should have the ability to specify the underlying type to aid
+  in portability and usability across platforms, across ABIs, and across
+  languages (for serialization and similar purposes). `N3030 Enhancements to Enumerations <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3030.htm>`_
 
 Non-comprehensive list of changes in this release
 -------------------------------------------------
@@ -211,6 +217,8 @@ Bug Fixes in This Version
 - Clang now doesn't produce false-positive warning `-Wconstant-logical-operand`
   for logical operators in C23.
   Fixes (`#64356 <https://github.com/llvm/llvm-project/issues/64356>`_).
+- Fixes miscompilation when an enum has a specified value such that the auto
+  increment overflows a signed long. Fixes (`#24667 <https://github.com/llvm/llvm-project/issues/24667>`_)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 10b5c271f25c10e..d9795589fbe5a38 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19797,6 +19797,19 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
           //     - If an initializer is specified for an enumerator, the
           //       initializing value has the same type as the expression.
           EltTy = Val->getType();
+        } else if (getLangOpts().C23) {
+          // C23 6.7.2.2p11 b4
+          // int, if given explicitly with = and the value of the
+          // integer constant expression is representable by an int
+          //
+          // C23 6.7.2.2p11 b5
+          // the type of the integer constant expression, if given
+          // explicitly with = and if the value of the integer
+          // constant expression is not representable by int;
+          if (isRepresentableIntegerValue(Context, EnumVal, Context.IntTy)) {
+            Val = ImpCastExprToType(Val, Context.IntTy, CK_IntegralCast).get();
+          }
+          EltTy = Val->getType();
         } else {
           // C99 6.7.2.2p2:
           //   The expression that defines the value of an enumeration constant
@@ -19806,8 +19819,8 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
           // Complain if the value is not representable in an int.
           if (!isRepresentableIntegerValue(Context, EnumVal, Context.IntTy))
             Diag(IdLoc, diag::ext_enum_value_not_int)
-              << toString(EnumVal, 10) << Val->getSourceRange()
-              << (EnumVal.isUnsigned() || EnumVal.isNonNegative());
+                << toString(EnumVal, 10) << Val->getSourceRange()
+                << (EnumVal.isUnsigned() || EnumVal.isNonNegative());
           else if (!Context.hasSameType(Val->getType(), Context.IntTy)) {
             // Force the type of the expression to 'int'.
             Val = ImpCastExprToType(Val, Context.IntTy, CK_IntegralCast).get();
@@ -19855,20 +19868,28 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
         //       sufficient to contain the incremented value. If no such type
         //       exists, the program is ill-formed.
         QualType T = getNextLargerIntegralType(Context, EltTy);
-        if (T.isNull() || Enum->isFixed()) {
+        if (Enum->isFixed()) {
           // There is no integral type larger enough to represent this
           // value. Complain, then allow the value to wrap around.
           EnumVal = LastEnumConst->getInitVal();
           EnumVal = EnumVal.zext(EnumVal.getBitWidth() * 2);
           ++EnumVal;
-          if (Enum->isFixed())
-            // When the underlying type is fixed, this is ill-formed.
-            Diag(IdLoc, diag::err_enumerator_wrapped)
-              << toString(EnumVal, 10)
-              << EltTy;
-          else
+          // When the underlying type is fixed, this is ill-formed.
+          Diag(IdLoc, diag::err_enumerator_wrapped)
+              << toString(EnumVal, 10) << EltTy;
+
+        } else if (T.isNull()) {
+          if (EltTy->isSignedIntegerType() && getLangOpts().CPlusPlus) {
+            EltTy = Context.getCorrespondingUnsignedType(EltTy);
+          } else {
+            // There is no integral type larger enough to represent this
+            // value. Complain, then allow the value to wrap around.
+            EnumVal = LastEnumConst->getInitVal();
+            EnumVal = EnumVal.zext(EnumVal.getBitWidth() * 2);
+            ++EnumVal;
             Diag(IdLoc, diag::ext_enumerator_increment_too_large)
-              << toString(EnumVal, 10);
+                << toString(EnumVal, 10);
+          }
         } else {
           EltTy = T;
         }
@@ -19886,14 +19907,26 @@ EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
         // an int (C99 6.7.2.2p2). However, we support GCC's extension that
         // permits enumerator values that are representable in some larger
         // integral type.
-        if (!getLangOpts().CPlusPlus && !T.isNull())
+        // starting in C23 standard C allows larger enum values
+        // C23 6.7.2.2p11
+        // - the type of the value from the previous enumeration constant with
+        //   one added to it. If such an integer constant expression would
+        //   overflow or wraparound the value of the previous enumeration
+        //   constant from the addition of one, the type takes on either:
+        // - - a suitably sized signed integer type, excluding the bit-precise
+        //     signed integer types, capable of representing the value of the
+        //     previous enumeration constant plus one; or,
+        // - - a suitably sized unsigned integer type, excluding the bit-precise
+        //     unsigned integer types, capable of representing the value of the
+        //     previous enumeration constant plus one.
+
+        if (!getLangOpts().CPlusPlus && !T.isNull() && !getLangOpts().C23)
           Diag(IdLoc, diag::warn_enum_value_overflow);
-      } else if (!getLangOpts().CPlusPlus &&
-                 !EltTy->isDependentType() &&
-                 !isRepresentableIntegerValue(Context, EnumVal, EltTy)) {
+      } else if (!getLangOpts().CPlusPlus && !EltTy->isDependentType() &&
+                 !isRepresentableIntegerValue(Context, EnumVal, EltTy) &&
+                 !getLangOpts().C23) {
         // Enforce C99 6.7.2.2p2 even when we compute the next value.
-        Diag(IdLoc, diag::ext_enum_value_not_int)
-          << toString(EnumVal, 10) << 1;
+        Diag(IdLoc, diag::ext_enum_value_not_int) << toString(EnumVal, 10) << 1;
       }
     }
   }
diff --git a/clang/test/Sema/enum.c b/clang/test/Sema/enum.c
index b0707914c0d8524..2c1cafc74d9cbb5 100644
--- a/clang/test/Sema/enum.c
+++ b/clang/test/Sema/enum.c
@@ -1,11 +1,11 @@
 // RUN: %clang_cc1 -triple %itanium_abi_triple %s -fsyntax-only -verify -pedantic
 // RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -fsyntax-only -std=c23 -verify -pedantic
+
+#if __STDC_VERSION__  < 202311L
 enum e {A,
         B = 42LL << 32,        // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
       C = -4, D = 12456 };
 
-enum f { a = -2147483648, b = 2147483647 }; // ok.
-
 enum g {  // too negative
    c = -2147483649,         // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
    d = 2147483647 };
@@ -19,6 +19,41 @@ enum x                      // expected-warning {{enumeration values exceed rang
 { y = -9223372036854775807LL-1,  // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
 z = 9223372036854775808ULL };    // expected-warning {{ISO C restricts enumerator values to range of 'int'}}
 
+
+enum GH24667 {   GH24667_x = 9223372036854775807UL, };
+// expected-warning at -1 {{ISO C restricts enumerator values to range of 'int' (9223372036854775807 is too large)}}
+
+#else
+enum e {A,
+        B = 42LL << 32,
+      C = -4, D = 12456 };
+
+// these should not raise warnings with C23 
+enum g {
+   c = -2147483649,         
+   d = 2147483647 };
+enum h { e = -2147483648,
+   f = 2147483648,           
+  i = 0xFFFF0000 
+};
+
+// minll maxull
+enum x                      // expected-warning {{enumeration values exceed range of largest integer}}
+{ y = -9223372036854775807LL-1,  
+z = 9223372036854775808ULL }; 
+
+enum GH24667 {   GH24667_x = 9223372036854775807UL, };
+enum N3029 { 	N3029_0 = 0xFFFFFFFFFFFFFFFFULL };
+enum N3030 : unsigned long long {
+	N3030_0 = 0xFFFFFFFFFFFFFFFFULL
+	// ^ not a constraint violation with a 64-bit unsigned long long
+};
+#endif
+
+enum f { a = -2147483648, b = 2147483647 }; // ok.
+
+
+
 int test(void) {
   return sizeof(enum e) ;
 }
@@ -172,10 +207,8 @@ enum class GH42372_2 {
 #if __STDC_VERSION__ >= 202311L
 // FIXME: GCC picks __uint128_t as the underlying type for the enumeration
 // value and Clang picks unsigned long long.
-// FIXME: Clang does not yet implement WG14 N3029, so the warning about
-// restricting enumerator values to 'int' is not correct.
 enum GH59352 { // expected-warning {{enumeration values exceed range of largest integer}}
- BigVal = 66666666666666666666wb // expected-warning {{ISO C restricts enumerator values to range of 'int' (66666666666666666666 is too large)}}
+ BigVal = 66666666666666666666wb
 };
 _Static_assert(BigVal == 66666666666666666666wb); /* expected-error {{static assertion failed due to requirement 'BigVal == 66666666666666666666wb'}}
                                                      expected-note {{expression evaluates to '11326434445538011818 == 66666666666666666666'}}
diff --git a/clang/test/SemaCXX/enum.cpp b/clang/test/SemaCXX/enum.cpp
index fc65fd16f8c302d..bee1d996f34fe89 100644
--- a/clang/test/SemaCXX/enum.cpp
+++ b/clang/test/SemaCXX/enum.cpp
@@ -125,3 +125,5 @@ struct PR28903 {
     })
   };
 };
+
+enum GH24667 { GH24667_x = 9223372036854775807 , GH24667_y };



More information about the cfe-commits mailing list