]> granicus.if.org Git - clang/commitdiff
First pass at implementing C++ enum semantics: calculate (and store) an
authorJohn McCall <rjmccall@apple.com>
Wed, 9 Dec 2009 09:09:27 +0000 (09:09 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 9 Dec 2009 09:09:27 +0000 (09:09 +0000)
"integer promotion" type associated with an enum decl, and use this type to
determine which type to promote to.  This type obeys C++ [conv.prom]p2 and
is therefore generally signed unless the range of the enumerators forces
it to be unsigned.

Kills off a lot of false positives from -Wsign-compare in C++, addressing
rdar://7455616

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90965 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/Decl.h
lib/AST/ASTContext.cpp
lib/AST/Decl.cpp
lib/Frontend/PCHReaderDecl.cpp
lib/Frontend/PCHWriterDecl.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaOverload.cpp
test/Sema/compare.c
test/SemaCXX/compare.cpp
test/SemaCXX/enum.cpp
test/SemaCXX/overload-call.cpp

index 7384a84ff152b48d6217881f1fecc8dd3c5129f4..ff2b30227860572d8d48b0bedac5cbd3ec73c13a 100644 (file)
@@ -1594,6 +1594,12 @@ class EnumDecl : public TagDecl {
   /// have a different type than this does.
   QualType IntegerType;
 
+  /// PromotionType - The integer type that values of this type should
+  /// promote to.  In C, enumerators are generally of an integer type
+  /// directly, but gcc-style large enumerators (and all enumerators
+  /// in C++) are of the enum type instead.
+  QualType PromotionType;
+
   /// \brief If the enumeration was instantiated from an enumeration
   /// within a class or function template, this pointer refers to the
   /// enumeration declared within the template.
@@ -1623,7 +1629,8 @@ public:
   /// declaration as being defined; it's enumerators have already been
   /// added (via DeclContext::addDecl). NewType is the new underlying
   /// type of the enumeration type.
-  void completeDefinition(ASTContext &C, QualType NewType);
+  void completeDefinition(ASTContext &C, QualType NewType,
+                          QualType PromotionType);
 
   // enumerator_iterator - Iterates through the enumerators of this
   // enumeration.
@@ -1637,6 +1644,13 @@ public:
     return enumerator_iterator(this->decls_end());
   }
 
+  /// getPromotionType - Return the integer type that enumerators
+  /// should promote to.
+  QualType getPromotionType() const { return PromotionType; }
+
+  /// \brief Set the promotion type.
+  void setPromotionType(QualType T) { PromotionType = T; }
+
   /// getIntegerType - Return the integer type this enum decl corresponds to.
   /// This returns a null qualtype for an enum forward definition.
   QualType getIntegerType() const { return IntegerType; }
index fcdd48716e83d8ad2bd96302d92e62a30abd6474..91fd166133873b46b42ae58d2c11c73ae3f94100 100644 (file)
@@ -2671,7 +2671,7 @@ int ASTContext::getFloatingTypeOrder(QualType LHS, QualType RHS) {
 unsigned ASTContext::getIntegerRank(Type *T) {
   assert(T->isCanonicalUnqualified() && "T should be canonicalized");
   if (EnumType* ET = dyn_cast<EnumType>(T))
-    T = ET->getDecl()->getIntegerType().getTypePtr();
+    T = ET->getDecl()->getPromotionType().getTypePtr();
 
   if (T->isSpecificBuiltinType(BuiltinType::WChar))
     T = getFromTargetType(Target.getWCharType()).getTypePtr();
@@ -2752,6 +2752,8 @@ QualType ASTContext::isPromotableBitField(Expr *E) {
 QualType ASTContext::getPromotedIntegerType(QualType Promotable) {
   assert(!Promotable.isNull());
   assert(Promotable->isPromotableIntegerType());
+  if (const EnumType *ET = Promotable->getAs<EnumType>())
+    return ET->getDecl()->getPromotionType();
   if (Promotable->isSignedIntegerType())
     return IntTy;
   uint64_t PromotableSize = getTypeSize(Promotable);
@@ -4358,6 +4360,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS) {
   if (LHSClass != RHSClass) {
     // C99 6.7.2.2p4: Each enumerated type shall be compatible with char,
     // a signed integer type, or an unsigned integer type.
+    // Compatibility is based on the underlying type, not the promotion
+    // type.
     if (const EnumType* ETy = LHS->getAs<EnumType>()) {
       if (ETy->getDecl()->getIntegerType() == RHSCan.getUnqualifiedType())
         return RHS;
@@ -4517,6 +4521,8 @@ unsigned ASTContext::getIntWidth(QualType T) {
   if (FixedWidthIntType *FWIT = dyn_cast<FixedWidthIntType>(T)) {
     return FWIT->getWidth();
   }
+  if (EnumType *ET = dyn_cast<EnumType>(T))
+    T = ET->getDecl()->getPromotionType();
   // For builtin types, just use the standard type sizing method
   return (unsigned)getTypeSize(T);
 }
index 42c9ef948cdc5033d579b383b5b097563aa96ffd..212dd353c31799dbe15cfa9d9b19636666bb1ff5 100644 (file)
@@ -195,9 +195,12 @@ void EnumDecl::Destroy(ASTContext& C) {
   Decl::Destroy(C);
 }
 
-void EnumDecl::completeDefinition(ASTContext &C, QualType NewType) {
+void EnumDecl::completeDefinition(ASTContext &C,
+                                  QualType NewType,
+                                  QualType NewPromotionType) {
   assert(!isDefinition() && "Cannot redefine enums!");
   IntegerType = NewType;
+  PromotionType = NewPromotionType;
   TagDecl::completeDefinition();
 }
 
index 40d48c53afff8d3a53fd7729800d55f226ed6462..01e1a4191a9917b48ea5c5ce36156383ee4f9402 100644 (file)
@@ -126,6 +126,7 @@ void PCHDeclReader::VisitTagDecl(TagDecl *TD) {
 void PCHDeclReader::VisitEnumDecl(EnumDecl *ED) {
   VisitTagDecl(ED);
   ED->setIntegerType(Reader.GetType(Record[Idx++]));
+  ED->setPromotionType(Reader.GetType(Record[Idx++]));
   // FIXME: C++ InstantiatedFrom
 }
 
index 1ba113e14e350f5b1998eb27ac45bd8990e82916..049cdb03ea3b3e5a1adaad10e7c2d91a0b97d035 100644 (file)
@@ -123,6 +123,7 @@ void PCHDeclWriter::VisitTagDecl(TagDecl *D) {
 void PCHDeclWriter::VisitEnumDecl(EnumDecl *D) {
   VisitTagDecl(D);
   Writer.AddTypeRef(D->getIntegerType(), Record);
+  Writer.AddTypeRef(D->getPromotionType(), Record);
   // FIXME: C++ InstantiatedFrom
   Code = pch::DECL_ENUM;
 }
index 617adfe36300aa8e84e0eec10958604650edbbc1..96b6c16485c87ce7acf9977ee0f8cf7a503c4574 100644 (file)
@@ -5766,6 +5766,17 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
   QualType BestType;
   unsigned BestWidth;
 
+  // C++0x N3000 [conv.prom]p3:
+  //   An rvalue of an unscoped enumeration type whose underlying
+  //   type is not fixed can be converted to an rvalue of the first
+  //   of the following types that can represent all the values of
+  //   the enumeration: int, unsigned int, long int, unsigned long
+  //   int, long long int, or unsigned long long int.
+  // C99 6.4.4.3p2:
+  //   An identifier declared as an enumeration constant has type int.
+  // The C99 rule is modified by a gcc extension 
+  QualType BestPromotionType;
+
   bool Packed = Enum->getAttr<PackedAttr>() ? true : false;
 
   if (NumNegativeBits) {
@@ -5773,22 +5784,21 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
     // int/long/longlong) that fits.
     // If it's packed, check also if it fits a char or a short.
     if (Packed && NumNegativeBits <= CharWidth && NumPositiveBits < CharWidth) {
-        BestType = Context.SignedCharTy;
-        BestWidth = CharWidth;
+      BestType = Context.SignedCharTy;
+      BestWidth = CharWidth;
     } else if (Packed && NumNegativeBits <= ShortWidth &&
                NumPositiveBits < ShortWidth) {
-        BestType = Context.ShortTy;
-        BestWidth = ShortWidth;
-    }
-    else if (NumNegativeBits <= IntWidth && NumPositiveBits < IntWidth) {
+      BestType = Context.ShortTy;
+      BestWidth = ShortWidth;
+    } else if (NumNegativeBits <= IntWidth && NumPositiveBits < IntWidth) {
       BestType = Context.IntTy;
       BestWidth = IntWidth;
     } else {
       BestWidth = Context.Target.getLongWidth();
 
-      if (NumNegativeBits <= BestWidth && NumPositiveBits < BestWidth)
+      if (NumNegativeBits <= BestWidth && NumPositiveBits < BestWidth) {
         BestType = Context.LongTy;
-      else {
+      else {
         BestWidth = Context.Target.getLongLongWidth();
 
         if (NumNegativeBits > BestWidth || NumPositiveBits >= BestWidth)
@@ -5796,31 +5806,46 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
         BestType = Context.LongLongTy;
       }
     }
+    BestPromotionType = (BestWidth <= IntWidth ? Context.IntTy : BestType);
   } else {
     // If there is no negative value, figure out which of uint, ulong, ulonglong
     // fits.
     // If it's packed, check also if it fits a char or a short.
     if (Packed && NumPositiveBits <= CharWidth) {
-        BestType = Context.UnsignedCharTy;
-        BestWidth = CharWidth;
+      BestType = Context.UnsignedCharTy;
+      BestPromotionType = Context.IntTy;
+      BestWidth = CharWidth;
     } else if (Packed && NumPositiveBits <= ShortWidth) {
-        BestType = Context.UnsignedShortTy;
-        BestWidth = ShortWidth;
-    }
-    else if (NumPositiveBits <= IntWidth) {
+      BestType = Context.UnsignedShortTy;
+      BestPromotionType = Context.IntTy;
+      BestWidth = ShortWidth;
+    else if (NumPositiveBits <= IntWidth) {
       BestType = Context.UnsignedIntTy;
       BestWidth = IntWidth;
+      BestPromotionType = (NumPositiveBits == BestWidth
+                           ? Context.UnsignedIntTy : Context.IntTy);
     } else if (NumPositiveBits <=
                (BestWidth = Context.Target.getLongWidth())) {
       BestType = Context.UnsignedLongTy;
+      BestPromotionType = (NumPositiveBits == BestWidth
+                           ? Context.UnsignedLongTy : Context.LongTy);
     } else {
       BestWidth = Context.Target.getLongLongWidth();
       assert(NumPositiveBits <= BestWidth &&
              "How could an initializer get larger than ULL?");
       BestType = Context.UnsignedLongLongTy;
+      BestPromotionType = (NumPositiveBits == BestWidth
+                           ? Context.UnsignedLongLongTy : Context.LongLongTy);
     }
   }
 
+  // If we're in C and the promotion type is larger than an int, just
+  // use the underlying type, which is generally the unsigned integer
+  // type of the same rank as the promotion type.  This is how the gcc
+  // extension works.
+  if (!getLangOptions().CPlusPlus && BestPromotionType != Context.IntTy)
+    BestPromotionType = BestType;
+
   // Loop over all of the enumerator constants, changing their types to match
   // the type of the enum if needed.
   for (unsigned i = 0; i != NumElements; ++i) {
@@ -5898,7 +5923,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc,
       ECD->setType(NewTy);
   }
 
-  Enum->completeDefinition(Context, BestType);
+  Enum->completeDefinition(Context, BestType, BestPromotionType);
 }
 
 Sema::DeclPtrTy Sema::ActOnFileScopeAsmDecl(SourceLocation Loc,
index 2555f5bde60af3da80d6a5e03557a8641381afba..4443de05380aff56935730f8ff3b257f862ad19a 100644 (file)
@@ -754,19 +754,21 @@ bool Sema::IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType) {
   // can be converted to an rvalue of the first of the following types
   // that can represent all the values of its underlying type: int,
   // unsigned int, long, or unsigned long (C++ 4.5p2).
-  if ((FromType->isEnumeralType() || FromType->isWideCharType())
-      && ToType->isIntegerType()) {
+
+  // We pre-calculate the promotion type for enum types.
+  if (const EnumType *FromEnumType = FromType->getAs<EnumType>())
+    if (ToType->isIntegerType())
+      return Context.hasSameUnqualifiedType(ToType,
+                                FromEnumType->getDecl()->getPromotionType());
+
+  if (FromType->isWideCharType() && ToType->isIntegerType()) {
     // Determine whether the type we're converting from is signed or
     // unsigned.
     bool FromIsSigned;
     uint64_t FromSize = Context.getTypeSize(FromType);
-    if (const EnumType *FromEnumType = FromType->getAs<EnumType>()) {
-      QualType UnderlyingType = FromEnumType->getDecl()->getIntegerType();
-      FromIsSigned = UnderlyingType->isSignedIntegerType();
-    } else {
-      // FIXME: Is wchar_t signed or unsigned? We assume it's signed for now.
-      FromIsSigned = true;
-    }
+    
+    // FIXME: Is wchar_t signed or unsigned? We assume it's signed for now.
+    FromIsSigned = true;
 
     // The types we'll try to promote to, in the appropriate
     // order. Try each of these types.
index 01a216ffec227d10ea3b5c7b3449c4833eacc176..fa2d3a062ed15624b7f941f1a6fbe365cea674d2 100644 (file)
@@ -225,3 +225,8 @@ int void_pointers(void* foo) {
   return foo == (void*) 0;
   return foo == (void*) 1;
 }
+
+int test1(int i) {
+  enum en { zero };
+  return i > zero;
+}
index e38b6b312f98e037af28a253ae3ea23b1adf87c7..7188678b74db82fe6db6cf561d0fad422f0f419f 100644 (file)
@@ -49,8 +49,8 @@ int test0(long a, unsigned long b) {
          ((signed char) A == (unsigned char) b) +
          (A < (unsigned long) b) +
          (A < (unsigned int) b) +
-         (A < (unsigned short) b) +  // expected-warning {{comparison of integers of different signs}}
-         (A < (unsigned char) b) +  // expected-warning {{comparison of integers of different signs}}
+         (A < (unsigned short) b) +
+         (A < (unsigned char) b) +
          ((long) A < b) +
          ((int) A < b) +
          ((short) A < b) +
@@ -78,9 +78,9 @@ int test0(long a, unsigned long b) {
          (a < (unsigned short) B) +
          (a < (unsigned char) B) +
          ((long) a < B) +
-         ((int) a < B) +  // expected-warning {{comparison of integers of different signs}}
-         ((short) a < B) +  // expected-warning {{comparison of integers of different signs}}
-         ((signed char) a < B) +  // expected-warning {{comparison of integers of different signs}}
+         ((int) a < B) +
+         ((short) a < B) +
+         ((signed char) a < B) +
          ((long) a < (unsigned long) B) +  // expected-warning {{comparison of integers of different signs}}
          ((int) a < (unsigned int) B) +  // expected-warning {{comparison of integers of different signs}}
          ((short) a < (unsigned short) B) +  // expected-warning {{comparison of integers of different signs}}
@@ -101,8 +101,8 @@ int test0(long a, unsigned long b) {
          ((signed char) C == (unsigned char) b) +
          (C < (unsigned long) b) +
          (C < (unsigned int) b) +
-         (C < (unsigned short) b) +  // expected-warning {{comparison of integers of different signs}}
-         (C < (unsigned char) b) +  // expected-warning {{comparison of integers of different signs}}
+         (C < (unsigned short) b) +
+         (C < (unsigned char) b) +
          ((long) C < b) +
          ((int) C < b) +
          ((short) C < b) +
@@ -130,9 +130,9 @@ int test0(long a, unsigned long b) {
          (a < (unsigned short) C) +
          (a < (unsigned char) C) +
          ((long) a < C) +
-         ((int) a < C) +  // expected-warning {{comparison of integers of different signs}}
-         ((short) a < C) +  // expected-warning {{comparison of integers of different signs}}
-         ((signed char) a < C) +  // expected-warning {{comparison of integers of different signs}}
+         ((int) a < C) +
+         ((short) a < C) +
+         ((signed char) a < C) +
          ((long) a < (unsigned long) C) +  // expected-warning {{comparison of integers of different signs}}
          ((int) a < (unsigned int) C) +  // expected-warning {{comparison of integers of different signs}}
          ((short) a < (unsigned short) C) +  // expected-warning {{comparison of integers of different signs}}
@@ -193,3 +193,8 @@ int test0(long a, unsigned long b) {
          10
     ;
 }
+
+int test1(int i) {
+  enum en { zero };
+  return i > zero;
+}
index 1aba107a69dad15dd12b2d7bd0645ac6e3da1c02..3315de00ca83e36b1f62c3641087d67c08464915 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: clang-cc -fsyntax-only -verify %s
+
 enum E {
   Val1,
   Val2
@@ -35,3 +36,32 @@ static enum e1 badfunc(struct s1 *q) {
 }
 
 enum e2; // expected-error{{ISO C++ forbids forward references to 'enum' types}}
+
+namespace test1 {
+  template <class A, class B> struct is_same { static const int value = -1; };
+  template <class A> struct is_same<A,A> { static const int value = 1; };
+
+  enum enum0 { v0 };
+  int test0[is_same<typeof(+v0), int>::value];
+
+  enum enum1 { v1 = __INT_MAX__ };
+  int test1[is_same<typeof(+v1), int>::value];
+
+  enum enum2 { v2 = __INT_MAX__ * 2U };
+  int test2[is_same<typeof(+v2), unsigned int>::value];
+
+  // This kindof assumes that 'int' is smaller than 'long long'.
+#if defined(__LP64__)
+  enum enum3 { v3 = __LONG_LONG_MAX__ };
+  int test3[is_same<typeof(+v3), long>::value];
+
+  enum enum4 { v4 = __LONG_LONG_MAX__ * 2ULL };
+  int test4[is_same<typeof(+v4), unsigned long>::value];
+#else
+  enum enum3 { v3 = __LONG_LONG_MAX__ };
+  int test3[is_same<typeof(+v3), long long>::value];
+
+  enum enum4 { v4 = __LONG_LONG_MAX__ * 2ULL };
+  int test4[is_same<typeof(+v4), unsigned long long>::value];  
+#endif
+}
index 3a0bf3008d6cb1e3b40d0670e5751b3fa35ffb0a..0936b947c4cad5096d7377ed54a8b93a4fb1423d 100644 (file)
@@ -92,7 +92,7 @@ enum PromotesToInt {
 };
 
 enum PromotesToUnsignedInt {
-  PromotesToUnsignedIntValue = 1u
+  PromotesToUnsignedIntValue = __INT_MAX__ * 2U
 };
 
 int* o(int);