From: John McCall Date: Wed, 9 Dec 2009 09:09:27 +0000 (+0000) Subject: First pass at implementing C++ enum semantics: calculate (and store) an X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=842aef8d942a880eeb9535d40de31a86838264cb;p=clang First pass at implementing C++ enum semantics: calculate (and store) an "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 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 7384a84ff1..ff2b302278 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -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; } diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index fcdd48716e..91fd166133 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -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(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()) + 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()) { if (ETy->getDecl()->getIntegerType() == RHSCan.getUnqualifiedType()) return RHS; @@ -4517,6 +4521,8 @@ unsigned ASTContext::getIntWidth(QualType T) { if (FixedWidthIntType *FWIT = dyn_cast(T)) { return FWIT->getWidth(); } + if (EnumType *ET = dyn_cast(T)) + T = ET->getDecl()->getPromotionType(); // For builtin types, just use the standard type sizing method return (unsigned)getTypeSize(T); } diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 42c9ef948c..212dd353c3 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -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(); } diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp index 40d48c53af..01e1a4191a 100644 --- a/lib/Frontend/PCHReaderDecl.cpp +++ b/lib/Frontend/PCHReaderDecl.cpp @@ -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 } diff --git a/lib/Frontend/PCHWriterDecl.cpp b/lib/Frontend/PCHWriterDecl.cpp index 1ba113e14e..049cdb03ea 100644 --- a/lib/Frontend/PCHWriterDecl.cpp +++ b/lib/Frontend/PCHWriterDecl.cpp @@ -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; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 617adfe363..96b6c16485 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -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() ? 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, diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 2555f5bde6..4443de0538 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -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()) + 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()) { - 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. diff --git a/test/Sema/compare.c b/test/Sema/compare.c index 01a216ffec..fa2d3a062e 100644 --- a/test/Sema/compare.c +++ b/test/Sema/compare.c @@ -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; +} diff --git a/test/SemaCXX/compare.cpp b/test/SemaCXX/compare.cpp index e38b6b312f..7188678b74 100644 --- a/test/SemaCXX/compare.cpp +++ b/test/SemaCXX/compare.cpp @@ -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; +} diff --git a/test/SemaCXX/enum.cpp b/test/SemaCXX/enum.cpp index 1aba107a69..3315de00ca 100644 --- a/test/SemaCXX/enum.cpp +++ b/test/SemaCXX/enum.cpp @@ -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 struct is_same { static const int value = -1; }; + template struct is_same { static const int value = 1; }; + + enum enum0 { v0 }; + int test0[is_same::value]; + + enum enum1 { v1 = __INT_MAX__ }; + int test1[is_same::value]; + + enum enum2 { v2 = __INT_MAX__ * 2U }; + int test2[is_same::value]; + + // This kindof assumes that 'int' is smaller than 'long long'. +#if defined(__LP64__) + enum enum3 { v3 = __LONG_LONG_MAX__ }; + int test3[is_same::value]; + + enum enum4 { v4 = __LONG_LONG_MAX__ * 2ULL }; + int test4[is_same::value]; +#else + enum enum3 { v3 = __LONG_LONG_MAX__ }; + int test3[is_same::value]; + + enum enum4 { v4 = __LONG_LONG_MAX__ * 2ULL }; + int test4[is_same::value]; +#endif +} diff --git a/test/SemaCXX/overload-call.cpp b/test/SemaCXX/overload-call.cpp index 3a0bf3008d..0936b947c4 100644 --- a/test/SemaCXX/overload-call.cpp +++ b/test/SemaCXX/overload-call.cpp @@ -92,7 +92,7 @@ enum PromotesToInt { }; enum PromotesToUnsignedInt { - PromotesToUnsignedIntValue = 1u + PromotesToUnsignedIntValue = __INT_MAX__ * 2U }; int* o(int);