From 4f89ccb1dfc8cd1b21db5571308bfda676068090 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Sat, 28 Jun 2008 06:23:08 +0000 Subject: [PATCH] Fix for PR2501; this patch makes usual arithmetic conversions for integers which have the same width and different signedness work correctly. (The testcase in PR2501 uses a comparison between long and unsigned int). llvm-svn: 52853 --- clang/include/clang/AST/ASTContext.h | 13 +++++++++++ clang/include/clang/AST/Type.h | 2 ++ clang/lib/AST/ASTContext.cpp | 35 ++++++++++++++++++++++++++++ clang/lib/AST/Type.cpp | 7 ++++++ clang/lib/Sema/SemaExpr.cpp | 33 ++++++++++++++++++++++---- clang/test/Sema/int-arith-convert.c | 12 ++++++++++ 6 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 clang/test/Sema/int-arith-convert.c diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 1fd65b1306f9..a9100956fcd6 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -370,6 +370,19 @@ public: return T->getAsStructureType() == SelStructType; } + //===--------------------------------------------------------------------===// + // Integer Predicates + //===--------------------------------------------------------------------===// + + // The width of an integer, as defined in C99 6.2.6.2. This is the number + // of bits in an integer type excluding any padding bits. + unsigned getIntWidth(QualType T); + + // Per C99 6.2.5p6, for every signed integer type, there is a corresponding + // unsigned integer type. This method takes a signed type, and returns the + // corresponding unsigned integer type. + QualType getCorrespondingUnsignedType(QualType T); + //===--------------------------------------------------------------------===// // Serialization //===--------------------------------------------------------------------===// diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 2e80f443f9b8..beedbfb2748b 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -48,6 +48,7 @@ namespace clang { class VariableArrayType; class IncompleteArrayType; class RecordType; + class EnumType; class ComplexType; class TagType; class TypedefType; @@ -352,6 +353,7 @@ public: const RecordType *getAsStructureType() const; const TypedefType *getAsTypedefType() const; const RecordType *getAsUnionType() const; + const EnumType *getAsEnumType() const; const VectorType *getAsVectorType() const; // GCC vector type. const ComplexType *getAsComplexType() const; const ComplexType *getAsComplexIntegerType() const; // GCC complex int type. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 11ca78a47986..08a2bb1357ba 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1756,6 +1756,41 @@ bool ASTContext::typesAreCompatible(QualType LHS_NC, QualType RHS_NC) { return true; // should never get here... } +//===----------------------------------------------------------------------===// +// Integer Predicates +//===----------------------------------------------------------------------===// +unsigned ASTContext::getIntWidth(QualType T) { + if (T == BoolTy) + return 1; + // At the moment, only bool has padding bits + return (unsigned)getTypeSize(T); +} + +QualType ASTContext::getCorrespondingUnsignedType(QualType T) { + assert(T->isSignedIntegerType() && "Unexpected type"); + if (const EnumType* ETy = T->getAsEnumType()) + T = ETy->getDecl()->getIntegerType(); + const BuiltinType* BTy = T->getAsBuiltinType(); + assert (BTy && "Unexpected signed integer type"); + switch (BTy->getKind()) { + case BuiltinType::Char_S: + case BuiltinType::SChar: + return UnsignedCharTy; + case BuiltinType::Short: + return UnsignedShortTy; + case BuiltinType::Int: + return UnsignedIntTy; + case BuiltinType::Long: + return UnsignedLongTy; + case BuiltinType::LongLong: + return UnsignedLongLongTy; + default: + assert(0 && "Unexpected signed integer type"); + return QualType(); + } +} + + //===----------------------------------------------------------------------===// // Serialization Support //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 7e09bb1ae37d..bf32c5306d76 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -390,6 +390,13 @@ const RecordType *Type::getAsUnionType() const { return 0; } +const EnumType *Type::getAsEnumType() const { + // Check the canonicalized unqualified type directly; the more complex + // version is unnecessary because there isn't any typedef information + // to preserve. + return dyn_cast(CanonicalType.getUnqualifiedType()); +} + const ComplexType *Type::getAsComplexType() const { // Are we directly a complex type? if (const ComplexType *CTy = dyn_cast(this)) diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 97513925fdaa..8ef948645909 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1221,12 +1221,35 @@ QualType Sema::UsualArithmeticConversions(Expr *&lhsExpr, Expr *&rhsExpr, } } // Finally, we have two differing integer types. - if (Context.getIntegerTypeOrder(lhs, rhs) >= 0) { // convert the rhs - if (!isCompAssign) ImpCastExprToType(rhsExpr, lhs); - return lhs; + // The rules for this case are in C99 6.3.1.8 + int compare = Context.getIntegerTypeOrder(lhs, rhs); + bool lhsSigned = lhs->isSignedIntegerType(), + rhsSigned = rhs->isSignedIntegerType(); + QualType destType; + if (lhsSigned == rhsSigned) { + // Same signedness; use the higher-ranked type + destType = compare >= 0 ? lhs : rhs; + } else if (compare != (lhsSigned ? 1 : -1)) { + // The unsigned type has greater than or equal rank to the + // signed type, so use the unsigned type + destType = lhsSigned ? rhs : lhs; + } else if (Context.getIntWidth(lhs) != Context.getIntWidth(rhs)) { + // The two types are different widths; if we are here, that + // means the signed type is larger than the unsigned type, so + // use the signed type. + destType = lhsSigned ? lhs : rhs; + } else { + // The signed type is higher-ranked than the unsigned type, + // but isn't actually any bigger (like unsigned int and long + // on most 32-bit systems). Use the unsigned type corresponding + // to the signed type. + destType = Context.getCorrespondingUnsignedType(lhsSigned ? lhs : rhs); } - if (!isCompAssign) ImpCastExprToType(lhsExpr, rhs); // convert the lhs - return rhs; + if (!isCompAssign) { + ImpCastExprToType(lhsExpr, destType); + ImpCastExprToType(rhsExpr, destType); + } + return destType; } // CheckPointerTypesForAssignment - This is a very tricky routine (despite diff --git a/clang/test/Sema/int-arith-convert.c b/clang/test/Sema/int-arith-convert.c new file mode 100644 index 000000000000..254c6c0591a5 --- /dev/null +++ b/clang/test/Sema/int-arith-convert.c @@ -0,0 +1,12 @@ +// RUN: clang -fsyntax-only -verify %s + +// Check types are the same through redeclaration +unsigned long x; +__typeof(1u+1l) x; + +unsigned y; +__typeof(1+1u) y; +__typeof(1u+1) y; + +long long z; +__typeof(1ll+1u) z;