[cfe-commits] r95031 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td lib/Sema/SemaDecl.cpp test/CXX/dcl.dcl/dcl.enum/p5.cpp test/Sema/enum.c

Douglas Gregor dgregor at apple.com
Mon Feb 1 15:36:03 PST 2010


Author: dgregor
Date: Mon Feb  1 17:36:03 2010
New Revision: 95031

URL: http://llvm.org/viewvc/llvm-project?rev=95031&view=rev
Log:
Improve handling of enumerator values for C and C++, including:

  - In C++, prior to the closing '}', set the type of enumerators
    based on the type of their initializer. Don't perform unary
    conversions on the enumerator values.
  - In C++, handle overflow when an enumerator has no initializer and
    its value cannot be represented in the type of the previous
    enumerator.
  - In C, handle overflow more gracefully, by complaining and then
    falling back to the C++ rules.
  - In C, if the enumerator value is representable in an int, convert the
    expression to the type 'int'.

Fixes PR5854 and PR4515.


Added:
    cfe/trunk/test/CXX/dcl.dcl/dcl.enum/p5.cpp
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/test/Sema/enum.c

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=95031&r1=95030&r2=95031&view=diff

==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Feb  1 17:36:03 2010
@@ -1460,9 +1460,13 @@
 def err_redefinition_of_enumerator : Error<"redefinition of enumerator %0">;
 def err_duplicate_member : Error<"duplicate member %0">;
 def ext_enum_value_not_int : Extension<
-  "ISO C restricts enumerator values to range of 'int' (%0 is too large)">;
+  "ISO C restricts enumerator values to range of 'int' (%0 is too "
+  "%select{small|large}1)">;
 def warn_enum_too_large : Warning<
   "enumeration values exceed range of largest integer">;
+def warn_enumerator_too_large : Warning<
+  "enumerator value %0 is not representable in the largest integer type">;
+  
 def warn_illegal_constant_array_size : Extension<
   "size of static array must be an integer constant expression">;
 def err_vla_decl_in_file_scope : Error<

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=95031&r1=95030&r2=95031&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Mon Feb  1 17:36:03 2010
@@ -5632,6 +5632,45 @@
     ProcessDeclAttributeList(S, Record, Attr);
 }
 
+/// \brief Determine whether the given integral value is representable within
+/// the given type T.
+static bool isRepresentableIntegerValue(ASTContext &Context,
+                                        llvm::APSInt &Value,
+                                        QualType T) {
+  assert(T->isIntegralType() && "Integral type required!");
+  unsigned BitWidth = Context.getTypeSize(T);
+  
+  if (Value.isUnsigned() || Value.isNonNegative())
+    return Value.getActiveBits() < BitWidth;
+  
+  return Value.getMinSignedBits() <= BitWidth;
+}
+
+// \brief Given an integral type, return the next larger integral type
+// (or a NULL type of no such type exists).
+static QualType getNextLargerIntegralType(ASTContext &Context, QualType T) {
+  // FIXME: Int128/UInt128 support, which also needs to be introduced into 
+  // enum checking below.
+  assert(T->isIntegralType() && "Integral type required!");
+  const unsigned NumTypes = 4;
+  QualType SignedIntegralTypes[NumTypes] = { 
+    Context.ShortTy, Context.IntTy, Context.LongTy, Context.LongLongTy
+  };
+  QualType UnsignedIntegralTypes[NumTypes] = { 
+    Context.UnsignedShortTy, Context.UnsignedIntTy, Context.UnsignedLongTy, 
+    Context.UnsignedLongLongTy
+  };
+  
+  unsigned BitWidth = Context.getTypeSize(T);
+  QualType *Types = T->isSignedIntegerType()? SignedIntegralTypes
+                                            : UnsignedIntegralTypes;
+  for (unsigned I = 0; I != NumTypes; ++I)
+    if (Context.getTypeSize(Types[I]) > BitWidth)
+      return Types[I];
+  
+  return QualType();
+}
+
 EnumConstantDecl *Sema::CheckEnumConstant(EnumDecl *Enum,
                                           EnumConstantDecl *LastEnumConst,
                                           SourceLocation IdLoc,
@@ -5639,24 +5678,45 @@
                                           ExprArg val) {
   Expr *Val = (Expr *)val.get();
 
-  llvm::APSInt EnumVal(32);
+  unsigned IntWidth = Context.Target.getIntWidth();
+  llvm::APSInt EnumVal(IntWidth);
   QualType EltTy;
   if (Val) {
     if (Enum->isDependentType())
       EltTy = Context.DependentTy;
     else {
-      // Make sure to promote the operand type to int.
-      UsualUnaryConversions(Val);
-      if (Val != val.get()) {
-        val.release();
-        val = Val;
-      }
-
       // C99 6.7.2.2p2: Make sure we have an integer constant expression.
       SourceLocation ExpLoc;
       if (VerifyIntegerConstantExpression(Val, &EnumVal)) {
         Val = 0;
-      } else {
+      } else {        
+        if (!getLangOptions().CPlusPlus) {
+          // C99 6.7.2.2p2:
+          //   The expression that defines the value of an enumeration constant
+          //   shall be an integer constant expression that has a value 
+          //   representable as an int.
+          
+          // Complain if the value is not representable in an int.
+          if (!isRepresentableIntegerValue(Context, EnumVal, Context.IntTy))
+            Diag(IdLoc, diag::ext_enum_value_not_int)
+              << EnumVal.toString(10) << Val->getSourceRange()
+              << EnumVal.isNonNegative();
+          else if (!Context.hasSameType(Val->getType(), Context.IntTy)) {
+            // Force the type of the expression to 'int'.
+            ImpCastExprToType(Val, Context.IntTy, CastExpr::CK_IntegralCast);
+            
+            if (Val != val.get()) {
+              val.release();
+              val = Val;
+            }
+          }
+        }
+        
+        // C++0x [dcl.enum]p5:
+        //   If the underlying type is not fixed, the type of each enumerator
+        //   is the type of its initializing value:
+        //     - If an initializer is specified for an enumerator, the 
+        //       initializing value has the same type as the expression.
         EltTy = Val->getType();
       }
     }
@@ -5665,25 +5725,76 @@
   if (!Val) {
     if (Enum->isDependentType())
       EltTy = Context.DependentTy;
-    else if (LastEnumConst) {
+    else if (!LastEnumConst) {
+      // C++0x [dcl.enum]p5:
+      //   If the underlying type is not fixed, the type of each enumerator
+      //   is the type of its initializing value:
+      //     - If no initializer is specified for the first enumerator, the 
+      //       initializing value has an unspecified integral type.
+      //
+      // GCC uses 'int' for its unspecified integral type, as does 
+      // C99 6.7.2.2p3.
+      EltTy = Context.IntTy;
+    } else {
       // Assign the last value + 1.
       EnumVal = LastEnumConst->getInitVal();
       ++EnumVal;
+      EltTy = LastEnumConst->getType();
 
       // Check for overflow on increment.
-      if (EnumVal < LastEnumConst->getInitVal())
-        Diag(IdLoc, diag::warn_enum_value_overflow);
-
-      EltTy = LastEnumConst->getType();
-    } else {
-      // First value, set to zero.
-      EltTy = Context.IntTy;
-      EnumVal.zextOrTrunc(static_cast<uint32_t>(Context.getTypeSize(EltTy)));
-      EnumVal.setIsSigned(true);
+      if (EnumVal < LastEnumConst->getInitVal()) {
+        // C++0x [dcl.enum]p5:
+        //   If the underlying type is not fixed, the type of each enumerator
+        //   is the type of its initializing value:
+        //
+        //     - Otherwise the type of the initializing value is the same as
+        //       the type of the initializing value of the preceding enumerator
+        //       unless the incremented value is not representable in that type,
+        //       in which case the type is an unspecified integral type 
+        //       sufficient to contain the incremented value. If no such type
+        //       exists, the program is ill-formed.
+        QualType T = getNextLargerIntegralType(Context, EltTy);
+        if (T.isNull()) {
+          // There is no integral type larger enough to represent this 
+          // value. Complain, then allow the value to wrap around.
+          EnumVal = LastEnumConst->getInitVal();
+          EnumVal.zext(EnumVal.getBitWidth() * 2);
+          Diag(IdLoc, diag::warn_enumerator_too_large)
+            << EnumVal.toString(10);
+        } else {
+          EltTy = T;
+        }
+        
+        // Retrieve the last enumerator's value, extent that type to the
+        // type that is supposed to be large enough to represent the incremented
+        // value, then increment.
+        EnumVal = LastEnumConst->getInitVal();
+        EnumVal.setIsSigned(EltTy->isSignedIntegerType());
+        EnumVal.zextOrTrunc(Context.getTypeSize(EltTy));
+        ++EnumVal;        
+        
+        // If we're not in C++, diagnose the overflow of enumerator values,
+        // which in C99 means that the enumerator value is not representable in
+        // 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 (!getLangOptions().CPlusPlus && !T.isNull())
+          Diag(IdLoc, diag::warn_enum_value_overflow);
+      } else if (!getLangOptions().CPlusPlus &&
+                 !isRepresentableIntegerValue(Context, EnumVal, EltTy)) {
+        // Enforce C99 6.7.2.2p2 even when we compute the next value.
+        Diag(IdLoc, diag::ext_enum_value_not_int)
+          << EnumVal.toString(10) << 1;
+      }
     }
   }
 
-  assert(!EltTy.isNull() && "Enum constant with NULL type");
+  if (!Enum->isDependentType()) {
+    // Make the enumerator value match the signedness and size of the 
+    // enumerator's type.
+    EnumVal.zextOrTrunc(Context.getTypeSize(EltTy));
+    EnumVal.setIsSigned(EltTy->isSignedIntegerType());
+  }
   
   val.release();
   return EnumConstantDecl::Create(Context, Enum, IdLoc, Id, EltTy,
@@ -5787,18 +5898,7 @@
       cast_or_null<EnumConstantDecl>(Elements[i].getAs<Decl>());
     if (!ECD) continue;  // Already issued a diagnostic.
 
-    // If the enum value doesn't fit in an int, emit an extension warning.
     const llvm::APSInt &InitVal = ECD->getInitVal();
-    assert(InitVal.getBitWidth() >= IntWidth &&
-           "Should have promoted value to int");
-    if (!getLangOptions().CPlusPlus && InitVal.getBitWidth() > IntWidth) {
-      llvm::APSInt V(InitVal);
-      V.trunc(IntWidth);
-      V.extend(InitVal.getBitWidth());
-      if (V != InitVal)
-        Diag(ECD->getLocation(), diag::ext_enum_value_not_int)
-          << InitVal.toString(10);
-    }
 
     // Keep track of the size of positive and negative values.
     if (InitVal.isUnsigned() || InitVal.isNonNegative())
@@ -5910,23 +6010,17 @@
     // enumerator value fits in an int, type it as an int, otherwise type it the
     // same as the enumerator decl itself.  This means that in "enum { X = 1U }"
     // that X has type 'int', not 'unsigned'.
-    if (!getLangOptions().CPlusPlus && ECD->getType() == Context.IntTy)
-      continue;
 
     // Determine whether the value fits into an int.
     llvm::APSInt InitVal = ECD->getInitVal();
-    bool FitsInInt;
-    if (InitVal.isUnsigned() || !InitVal.isNegative())
-      FitsInInt = InitVal.getActiveBits() < IntWidth;
-    else
-      FitsInInt = InitVal.getMinSignedBits() <= IntWidth;
 
     // If it fits into an integer type, force it.  Otherwise force it to match
     // the enum decl type.
     QualType NewTy;
     unsigned NewWidth;
     bool NewSign;
-    if (FitsInInt && !getLangOptions().CPlusPlus) {
+    if (!getLangOptions().CPlusPlus &&
+        isRepresentableIntegerValue(Context, InitVal, Context.IntTy)) {
       NewTy = Context.IntTy;
       NewWidth = IntWidth;
       NewSign = true;

Added: cfe/trunk/test/CXX/dcl.dcl/dcl.enum/p5.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.enum/p5.cpp?rev=95031&view=auto

==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.enum/p5.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.enum/p5.cpp Mon Feb  1 17:36:03 2010
@@ -0,0 +1,56 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10.0.0 -fsyntax-only -verify %s
+template<typename T> int force_same(T, T);
+
+// C++ [dcl.enum]p5:
+//   [...] If the underlying type is not fixed, the type of each enumerator is 
+//   the type of its initializing value:
+//     - If an initializer is specified for an enumerator, the initializing 
+//       value has the same type as the expression.
+enum Bullet1 {
+  Bullet1Val1 = 'a',
+  Bullet1Val2 = 10u,
+  Bullet1Val1IsChar = sizeof(force_same(Bullet1Val1, char(0))),
+  Bullet1Val2IsUnsigned = sizeof(force_same(Bullet1Val2, unsigned(0)))
+};
+
+//    - If no initializer is specified for the first enumerator, the 
+//      initializing value has an unspecified integral type.
+enum Bullet2 {
+  Bullet2Val,
+  Bullet2ValIsInt = sizeof(force_same(Bullet2Val, int(0)))
+};
+
+//    - Otherwise the type of the initializing value is the same as the type
+//      of the initializing value of the preceding enumerator unless the 
+//      incremented value is not representable in that type, in which case the
+//      type is an unspecified integral type sufficient to contain the 
+//      incremented value. If no such type exists, the program is ill-formed.
+enum Bullet3a {
+  Bullet3aVal1 = 17,
+  Bullet3aVal2,
+  Bullet3aVal2IsInt = sizeof(force_same(Bullet3aVal2, int(0))),
+  Bullet3aVal3 = 2147483647,
+  Bullet3aVal3IsInt = sizeof(force_same(Bullet3aVal3, int(0))),
+  Bullet3aVal4,
+  Bullet3aVal4IsUnsigned = sizeof(force_same(Bullet3aVal4, 0ul))
+};
+
+enum Bullet3b {
+  Bullet3bVal1 = 17u,
+  Bullet3bVal2,
+  Bullet3bVal2IsInt = sizeof(force_same(Bullet3bVal2, 0u)),
+  Bullet3bVal3 = 2147483647u,
+  Bullet3bVal3IsInt = sizeof(force_same(Bullet3bVal3, 0u)),
+  Bullet3bVal4,
+  Bullet3bVal4IsUnsigned = sizeof(force_same(Bullet3bVal4, 0ul))
+};
+
+enum Bullet3c {
+  Bullet3cVal1 = 0xFFFFFFFFFFFFFFFEull,
+  Bullet3cVal2,
+  Bullet3cVal3 // expected-warning{{not representable}}
+};
+
+//   Following the closing brace of an enum-specifier, each enumerator has the
+//   type of its enumeration.
+int array0[sizeof(force_same(Bullet3bVal3, Bullet3b(0)))? 1 : -1];

Modified: cfe/trunk/test/Sema/enum.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/enum.c?rev=95031&r1=95030&r2=95031&view=diff

==============================================================================
--- cfe/trunk/test/Sema/enum.c (original)
+++ cfe/trunk/test/Sema/enum.c Mon Feb  1 17:36:03 2010
@@ -92,3 +92,7 @@
 } an_enum;
 // FIXME: why is this only a warning?
 char * s = (an_enum) an_enumerator; // expected-warning {{incompatible integer to pointer conversion initializing 'an_enum', expected 'char *'}}
+
+// PR4515
+enum PR4515 {PR4515a=1u,PR4515b=(PR4515a-2)/2};
+int CheckPR4515[PR4515b==0?1:-1];





More information about the cfe-commits mailing list