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

via cfe-commits cfe-commits at lists.llvm.org
Sun Feb 18 05:41:34 PST 2024


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

>From 6bbd4953ff86a120097fae7bb83e00989392c14f 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 enums overflowing (#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 |  9 +++++
 clang/lib/Sema/SemaDecl.cpp | 65 ++++++++++++++++++++++++++++---------
 clang/test/Sema/enum.c      | 41 +++++++++++++++++++++--
 clang/test/SemaCXX/enum.cpp |  2 ++
 4 files changed, 98 insertions(+), 19 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 06c7d57d73ca70..35b7e314b0ceb7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -135,6 +135,13 @@ C23 Feature Support
 
   Fixes (`#81472 <https://github.com/llvm/llvm-project/issues/81472>`_).
 
+  - 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
 -------------------------------------------------
 
@@ -207,6 +214,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 0aaaba0e5d15f9..34c07d32a551aa 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -19781,6 +19781,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
@@ -19790,8 +19803,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();
@@ -19839,20 +19852,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;
         }
@@ -19870,14 +19891,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 f8e380bd62d1ea..99ee288833fc26 100644
--- a/clang/test/Sema/enum.c
+++ b/clang/test/Sema/enum.c
@@ -1,10 +1,11 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple %s -fsyntax-only -verify -pedantic
+// RUN: %clang_cc1 -std=c23 -triple %itanium_abi_triple %s -fsyntax-only -verify -pedantic
+// RUN: %clang_cc1 -std=c17 -triple %itanium_abi_triple %s -fsyntax-only -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 };
@@ -18,6 +19,40 @@ 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 };
+
+enum g {  // too negative
+   c = -2147483649,         
+   d = 2147483647 };
+enum h { e = -2147483648, // too pos
+   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) ;
 }
diff --git a/clang/test/SemaCXX/enum.cpp b/clang/test/SemaCXX/enum.cpp
index fc65fd16f8c302..bee1d996f34fe8 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