From: Chandler Carruth Date: Sun, 24 Apr 2011 02:49:34 +0000 (+0000) Subject: Implement most of the remaining logic in __is_literal type trait. This X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9b6347cd410be55425f7062d22fd6e4ecb4e1a58;p=clang Implement most of the remaining logic in __is_literal type trait. This should now support all of the C++98 types, and all of the C++0x types Clang supports. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130079 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index 5d2a374f72..419e84e846 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -1479,6 +1479,10 @@ public: bool isPure() const { return IsPure; } void setPure(bool P = true); + /// Whether this is a constexpr function or constexpr constructor. + // FIXME: C++0x: Implement tracking of the constexpr specifier. + bool isConstExpr() const { return false; } + /// Whether this templated function will be late parsed. bool isLateTemplateParsed() const { return IsLateTemplateParsed; } void setLateTemplateParsed(bool ILT = true) { IsLateTemplateParsed = ILT; } diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 946f3a3000..f41a7d617e 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -316,6 +316,11 @@ class CXXRecordDecl : public RecordDecl { /// (or array thereof), each such class has a trivial constructor. bool HasTrivialConstructor : 1; + /// HasConstExprNonCopyMoveConstructor - True when this class has at least + /// one constexpr constructor which is neither the copy nor move + /// constructor. + bool HasConstExprNonCopyMoveConstructor : 1; + /// HasTrivialCopyConstructor - True when this class has a trivial copy /// constructor. /// @@ -386,6 +391,10 @@ class CXXRecordDecl : public RecordDecl { /// type (or array thereof), each such class has a trivial destructor. bool HasTrivialDestructor : 1; + /// HasNonLiteralTypeFieldsOrBases - True when this class contains at least + /// one non-static data member or base class of non literal type. + bool HasNonLiteralTypeFieldsOrBases : 1; + /// ComputedVisibleConversions - True when visible conversion functions are /// already computed and are available. bool ComputedVisibleConversions : 1; @@ -760,6 +769,12 @@ public: // (C++ [class.ctor]p5) bool hasTrivialConstructor() const { return data().HasTrivialConstructor; } + // hasConstExprNonCopyMoveConstructor - Whether this class has at least one + // constexpr constructor other than the copy or move constructors + bool hasConstExprNonCopyMoveConstructor() const { + return data().HasConstExprNonCopyMoveConstructor; + } + // hasTrivialCopyConstructor - Whether this class has a trivial copy // constructor (C++ [class.copy]p6, C++0x [class.copy]p13) bool hasTrivialCopyConstructor() const { @@ -788,6 +803,12 @@ public: // (C++ [class.dtor]p3) bool hasTrivialDestructor() const { return data().HasTrivialDestructor; } + // hasNonLiteralTypeFieldsOrBases - Whether this class has a non-literal type + // non-static data member or base class. + bool hasNonLiteralTypeFieldsOrBases() const { + return data().HasNonLiteralTypeFieldsOrBases; + } + // isTriviallyCopyable - Whether this class is considered trivially copyable // (C++0x [class]p5). bool isTriviallyCopyable() const; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index af796d54c4..a71750bb4c 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -32,9 +32,11 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false), Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false), Abstract(false), HasTrivialConstructor(true), + HasConstExprNonCopyMoveConstructor(false), HasTrivialCopyConstructor(true), HasTrivialMoveConstructor(true), HasTrivialCopyAssignment(true), HasTrivialMoveAssignment(true), - HasTrivialDestructor(true), ComputedVisibleConversions(false), + HasTrivialDestructor(true), HasNonLiteralTypeFieldsOrBases(false), + ComputedVisibleConversions(false), DeclaredDefaultConstructor(false), DeclaredCopyConstructor(false), DeclaredCopyAssignment(false), DeclaredDestructor(false), NumBases(0), NumVBases(0), Bases(), VBases(), @@ -117,6 +119,10 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // polymorphic class. if (BaseClassDecl->isPolymorphic()) data().Polymorphic = true; + + // Record if this base is the first non-literal field or base. + if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType()) + data().HasNonLiteralTypeFieldsOrBases = true; // Now go through all virtual bases of this base and add them. for (CXXRecordDecl::base_class_iterator VBase = @@ -486,6 +492,12 @@ void CXXRecordDecl::addedMember(Decl *D) { data().HasTrivialMoveConstructor = false; } } + if (Constructor->isConstExpr() && + !Constructor->isCopyOrMoveConstructor()) { + // Record if we see any constexpr constructors which are niether copy + // nor move constructors. + data().HasConstExprNonCopyMoveConstructor = true; + } return; } @@ -620,7 +632,11 @@ void CXXRecordDecl::addedMember(Decl *D) { data().PlainOldData = false; if (T->isReferenceType()) data().HasTrivialConstructor = false; - + + // Record if this field is the first non-literal field or base. + if (!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType()) + data().HasNonLiteralTypeFieldsOrBases = true; + if (const RecordType *RecordTy = T->getAs()) { CXXRecordDecl* FieldRec = cast(RecordTy->getDecl()); if (FieldRec->getDefinition()) { diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index a25f0331f4..124bf13305 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -874,32 +874,43 @@ bool Type::isLiteralType() const { // C++0x [basic.types]p10: // A type is a literal type if it is: - switch (CanonicalType->getTypeClass()) { - // We're whitelisting - default: return false; - - // -- a scalar type - case Builtin: - case Complex: - case Pointer: - case MemberPointer: - case Vector: - case ExtVector: - case ObjCObjectPointer: - case Enum: - return true; - - // -- a class type with ... - case Record: - // FIXME: Do the tests + // [...] + // -- an array of literal type + // Extension: variable arrays cannot be literal types, since they're + // runtime-sized. + if (isArrayType() && !isConstantArrayType()) return false; + const Type *BaseTy = getBaseElementTypeUnsafe(); + assert(BaseTy && "NULL element type"); - // -- an array of literal type - // Extension: variable arrays cannot be literal types, since they're - // runtime-sized. - case ConstantArray: - return cast(CanonicalType)->getElementType()->isLiteralType(); + // C++0x [basic.types]p10: + // A type is a literal type if it is: + // -- a scalar type; or + if (BaseTy->isScalarType()) return true; + // -- a reference type; or + if (BaseTy->isReferenceType()) return true; + // -- a class type that has all of the following properties: + if (const RecordType *RT = BaseTy->getAs()) { + const CXXRecordDecl *ClassDecl = cast(RT->getDecl()); + // -- a trivial destructor, + if (!ClassDecl->hasTrivialDestructor()) return false; + // -- every constructor call and full-expression in the + // brace-or-equal-initializers for non-static data members (if any) + // is a constant expression, + // FIXME: C++0x: Clang doesn't yet support non-static data member + // declarations with initializers, or constexprs. + // -- it is an aggregate type or has at least one constexpr + // constructor or constructor template that is not a copy or move + // constructor, and + if (!ClassDecl->isAggregate() && + !ClassDecl->hasConstExprNonCopyMoveConstructor()) + return false; + // -- all non-static data members and base classes of literal types + if (ClassDecl->hasNonLiteralTypeFieldsOrBases()) return false; + + return true; } + return false; } bool Type::isTrivialType() const { diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index a85099d5ef..8190eabc00 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -842,11 +842,13 @@ void ASTDeclReader::ReadCXXDefinitionData( Data.Polymorphic = Record[Idx++]; Data.Abstract = Record[Idx++]; Data.HasTrivialConstructor = Record[Idx++]; + Data.HasConstExprNonCopyMoveConstructor = Record[Idx++]; Data.HasTrivialCopyConstructor = Record[Idx++]; Data.HasTrivialMoveConstructor = Record[Idx++]; Data.HasTrivialCopyAssignment = Record[Idx++]; Data.HasTrivialMoveAssignment = Record[Idx++]; Data.HasTrivialDestructor = Record[Idx++]; + Data.HasNonLiteralTypeFieldsOrBases = Record[Idx++]; Data.ComputedVisibleConversions = Record[Idx++]; Data.DeclaredDefaultConstructor = Record[Idx++]; Data.DeclaredCopyConstructor = Record[Idx++]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 24a9c66755..7196a72718 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -3794,11 +3794,13 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec Record.push_back(Data.Polymorphic); Record.push_back(Data.Abstract); Record.push_back(Data.HasTrivialConstructor); + Record.push_back(Data.HasConstExprNonCopyMoveConstructor); Record.push_back(Data.HasTrivialCopyConstructor); Record.push_back(Data.HasTrivialMoveConstructor); Record.push_back(Data.HasTrivialCopyAssignment); Record.push_back(Data.HasTrivialMoveAssignment); Record.push_back(Data.HasTrivialDestructor); + Record.push_back(Data.HasNonLiteralTypeFieldsOrBases); Record.push_back(Data.ComputedVisibleConversions); Record.push_back(Data.DeclaredDefaultConstructor); Record.push_back(Data.DeclaredCopyConstructor); diff --git a/test/SemaCXX/literal-type.cpp b/test/SemaCXX/literal-type.cpp index 8eff677a0f..19f07d390b 100644 --- a/test/SemaCXX/literal-type.cpp +++ b/test/SemaCXX/literal-type.cpp @@ -8,4 +8,60 @@ static_assert(__is_literal(E), "fail"); static_assert(__is_literal(decltype(E1)), "fail"); typedef int IAR[10]; static_assert(__is_literal(IAR), "fail"); -// FIXME: Records + +// C++0x [basic.types]p10: +// A type is a literal type if it is: +// [...] +// -- a class type that has all of the following properties: +// -- it has a trivial destructor +// -- every constructor call and full-expression in the +// brace-or-equal-initializers for non-static data members (if an) is +// a constant expression, +// -- it is an aggregate type or has at least one constexpr constructor +// or constructor template that is not a copy or move constructor, and +// -- it has all non-static data members and base classes of literal +// types +struct Empty {}; +struct LiteralType { + int x; + E e; + IAR arr; + Empty empty; + int method(); +}; +struct HasDtor { ~HasDtor(); }; + +class NonAggregate { int x; }; +struct HasNonLiteralBase : NonAggregate {}; +struct HasNonLiteralMember { HasDtor x; }; + +static_assert(__is_literal(Empty), "fail"); +static_assert(__is_literal(LiteralType), "fail"); +static_assert(!__is_literal(HasDtor), "fail"); +static_assert(!__is_literal(NonAggregate), "fail"); +static_assert(!__is_literal(HasNonLiteralBase), "fail"); +static_assert(!__is_literal(HasNonLiteralMember), "fail"); + +// FIXME: Test constexpr constructors and non-static members with initializers +// when Clang supports them: +#if 0 +extern int f(); +struct HasNonConstExprMemInit { + int x = f(); + constexpr HasNonConstExprMemInit(int y) {} +}; +static_assert(!__is_literal(HasNonConstExprMemInit), "fail"); + +class HasConstExprCtor { + int x; +public: + constexpr HasConstExprCtor(int x) : x(x) {} +}; +template class HasConstExprCtorTemplate { + T x; +public: + template constexpr HasConstExprCtorTemplate(U y) : x(y) {} +}; +static_assert(__is_literal(HasConstExprCtor), "fail"); +static_assert(__is_literal(HasConstExprCtorTemplate), "fail"); +#endif