From: Eli Friedman Date: Wed, 26 Oct 2011 07:22:48 +0000 (+0000) Subject: Correctly perform integral promotions on wchar_t/char16_t/char32_t in C++. . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@143019 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index ae28149407..cb4e09bfc9 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -3549,18 +3549,6 @@ int ASTContext::getFloatingTypeOrder(QualType LHS, QualType RHS) const { /// or if it is not canonicalized. unsigned ASTContext::getIntegerRank(const Type *T) const { assert(T->isCanonicalUnqualified() && "T should be canonicalized"); - if (const EnumType* ET = dyn_cast(T)) - T = ET->getDecl()->getPromotionType().getTypePtr(); - - if (T->isSpecificBuiltinType(BuiltinType::WChar_S) || - T->isSpecificBuiltinType(BuiltinType::WChar_U)) - T = getFromTargetType(Target->getWCharType()).getTypePtr(); - - if (T->isSpecificBuiltinType(BuiltinType::Char16)) - T = getFromTargetType(Target->getChar16Type()).getTypePtr(); - - if (T->isSpecificBuiltinType(BuiltinType::Char32)) - T = getFromTargetType(Target->getChar32Type()).getTypePtr(); switch (cast(T)->getKind()) { default: llvm_unreachable("getIntegerRank(): not a built-in integer"); @@ -3630,6 +3618,34 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const { assert(Promotable->isPromotableIntegerType()); if (const EnumType *ET = Promotable->getAs()) return ET->getDecl()->getPromotionType(); + + if (const BuiltinType *BT = Promotable->getAs()) { + // C++ [conv.prom]: A prvalue of type char16_t, char32_t, or wchar_t + // (3.9.1) can be converted to a prvalue of the first of the following + // types that can represent all the values of its underlying type: + // int, unsigned int, long int, unsigned long int, long long int, or + // unsigned long long int [...] + // FIXME: Is there some better way to compute this? + if (BT->getKind() == BuiltinType::WChar_S || + BT->getKind() == BuiltinType::WChar_U || + BT->getKind() == BuiltinType::Char16 || + BT->getKind() == BuiltinType::Char32) { + bool FromIsSigned = BT->getKind() == BuiltinType::WChar_S; + uint64_t FromSize = getTypeSize(BT); + QualType PromoteTypes[] = { IntTy, UnsignedIntTy, LongTy, UnsignedLongTy, + LongLongTy, UnsignedLongLongTy }; + for (size_t Idx = 0; Idx < llvm::array_lengthof(PromoteTypes); ++Idx) { + uint64_t ToSize = getTypeSize(PromoteTypes[Idx]); + if (FromSize < ToSize || + (FromSize == ToSize && + FromIsSigned == PromoteTypes[Idx]->isSignedIntegerType())) + return PromoteTypes[Idx]; + } + llvm_unreachable("char type should fit into long long"); + } + } + + // At this point, we should have a signed or unsigned integer type. if (Promotable->isSignedIntegerType()) return IntTy; uint64_t PromotableSize = getTypeSize(Promotable); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index 0bac5f4f61..e01346cdb6 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -1272,6 +1272,10 @@ bool Type::isPromotableIntegerType() const { case BuiltinType::UChar: case BuiltinType::Short: case BuiltinType::UShort: + case BuiltinType::WChar_S: + case BuiltinType::WChar_U: + case BuiltinType::Char16: + case BuiltinType::Char32: return true; default: return false; @@ -1284,10 +1288,7 @@ bool Type::isPromotableIntegerType() const { || ET->getDecl()->isScoped()) return false; - const BuiltinType *BT - = ET->getDecl()->getPromotionType()->getAs(); - return BT->getKind() == BuiltinType::Int - || BT->getKind() == BuiltinType::UInt; + return true; } return false; diff --git a/test/CXX/conv/conv.prom/p2.cpp b/test/CXX/conv/conv.prom/p2.cpp new file mode 100644 index 0000000000..8d75419878 --- /dev/null +++ b/test/CXX/conv/conv.prom/p2.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x -ffreestanding %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x -fshort-wchar -ffreestanding %s + +#include + +// In theory, the promoted types vary by platform; however, in reality they +// are quite consistent across all platforms where clang runs. + +extern int promoted_wchar; +extern decltype(+L'a') promoted_wchar; + +extern int promoted_char16; +extern decltype(+u'a') promoted_char16; + +extern unsigned promoted_char32; +extern decltype(+U'a') promoted_char32;