From: Sebastian Redl Date: Mon, 5 Jan 2009 20:52:13 +0000 (+0000) Subject: PODness and Type Traits X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=64b45f7e0d3167f040841ac2920aead7f080730d;p=clang PODness and Type Traits Make C++ classes track the POD property (C++ [class]p4) Track the existence of a copy assignment operator. Implicitly declare the copy assignment operator if none is provided. Implement most of the parsing job for the G++ type traits extension. Fully implement the low-hanging fruit of the type traits: __is_pod: Whether a type is a POD. __is_class: Whether a type is a (non-union) class. __is_union: Whether a type is a union. __is_enum: Whether a type is an enum. __is_polymorphic: Whether a type is polymorphic (C++ [class.virtual]p1). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@61746 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 6f156fa827..583f57854a 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -22,6 +22,7 @@ class CXXRecordDecl; class CXXConstructorDecl; class CXXDestructorDecl; class CXXConversionDecl; +class CXXMethodDecl; /// TemplateTypeParmDecl - Declaration of a template type parameter, /// e.g., "T" in @@ -263,6 +264,10 @@ class CXXRecordDecl : public RecordDecl { /// user-declared copy constructor. bool UserDeclaredCopyConstructor : 1; + /// UserDeclaredCopyAssignment - True when this class has a + /// user-declared copy assignment operator. + bool UserDeclaredCopyAssignment : 1; + /// UserDeclaredDestructor - True when this class has a /// user-declared destructor. bool UserDeclaredDestructor : 1; @@ -270,6 +275,9 @@ class CXXRecordDecl : public RecordDecl { /// Aggregate - True when this class is an aggregate. bool Aggregate : 1; + /// PlainOldData - True when this class is a POD-type. + bool PlainOldData : 1; + /// Polymorphic - True when this class is polymorphic, i.e. has at least one /// virtual member or derives from a polymorphic class. bool Polymorphic : 1; @@ -321,6 +329,10 @@ public: /// copy constructor that accepts a const-qualified argument. bool hasConstCopyConstructor(ASTContext &Context) const; + /// hasConstCopyAssignment - Determines whether this class has a + /// copy assignment operator that accepts a const-qualified argument. + bool hasConstCopyAssignment(ASTContext &Context) const; + /// addedConstructor - Notify the class that another constructor has /// been added. This routine helps maintain information about the /// class based on which constructors have been added. @@ -338,6 +350,18 @@ public: return UserDeclaredCopyConstructor; } + /// addedAssignmentOperator - Notify the class that another assignment + /// operator has been added. This routine helps maintain information about the + /// class based on which operators have been added. + void addedAssignmentOperator(ASTContext &Context, CXXMethodDecl *OpDecl); + + /// hasUserDeclaredCopyAssignment - Whether this class has a + /// user-declared copy assignment operator. When false, a copy + /// assigment operator will be implicitly declared. + bool hasUserDeclaredCopyAssignment() const { + return UserDeclaredCopyAssignment; + } + /// hasUserDeclaredDestructor - Whether this class has a /// user-declared destructor. When false, a destructor will be /// implicitly declared. @@ -373,6 +397,15 @@ public: /// [dcl.init.aggr]). void setAggregate(bool Agg) { Aggregate = Agg; } + /// isPOD - Whether this class is a POD-type (C++ [class]p4), which is a class + /// that is an aggregate that has no non-static non-POD data members, no + /// reference data members, no user-defined copy assignment operator and no + /// user-defined destructor. + bool isPOD() const { return PlainOldData; } + + /// setPOD - Set whether this class is a POD-type (C++ [class]p4). + void setPOD(bool POD) { PlainOldData = POD; } + /// isPolymorphic - Whether this class is polymorphic (C++ [class.virtual]), /// which means that the class contains or inherits a virtual function. bool isPolymorphic() const { return Polymorphic; } diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index be33eb1939..37436575a2 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -14,6 +14,7 @@ #ifndef LLVM_CLANG_AST_EXPRCXX_H #define LLVM_CLANG_AST_EXPRCXX_H +#include "clang/Basic/TypeTraits.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" @@ -701,6 +702,51 @@ public: static CXXDependentNameExpr *CreateImpl(llvm::Deserializer& D, ASTContext& C); }; +/// UnaryTypeTraitExpr - A GCC or MS unary type trait, as used in the +/// implementation of TR1/C++0x type trait templates. +/// Example: +/// __is_pod(int) == true +/// __is_enum(std::string) == false +class UnaryTypeTraitExpr : public Expr { + /// UTT - The trait. + UnaryTypeTrait UTT; + + /// Loc - The location of the type trait keyword. + SourceLocation Loc; + + /// RParen - The location of the closing paren. + SourceLocation RParen; + + /// QueriedType - The type we're testing. + QualType QueriedType; + +public: + UnaryTypeTraitExpr(SourceLocation loc, UnaryTypeTrait utt, QualType queried, + SourceLocation rparen, QualType ty) + : Expr(UnaryTypeTraitExprClass, ty, false, queried->isDependentType()), + UTT(utt), Loc(loc), RParen(rparen), QueriedType(queried) { } + + virtual SourceRange getSourceRange() const { return SourceRange(Loc, RParen);} + + UnaryTypeTrait getTrait() const { return UTT; } + + QualType getQueriedType() const { return QueriedType; } + + bool Evaluate() const; + + static bool classof(const Stmt *T) { + return T->getStmtClass() == UnaryTypeTraitExprClass; + } + static bool classof(const UnaryTypeTraitExpr *) { return true; } + + // Iterators + virtual child_iterator child_begin(); + virtual child_iterator child_end(); + + virtual void EmitImpl(llvm::Serializer& S) const; + static UnaryTypeTraitExpr *CreateImpl(llvm::Deserializer& D, ASTContext& C); +}; + } // end namespace clang #endif diff --git a/include/clang/AST/StmtNodes.def b/include/clang/AST/StmtNodes.def index 27b7848d16..7bd378220b 100644 --- a/include/clang/AST/StmtNodes.def +++ b/include/clang/AST/StmtNodes.def @@ -113,6 +113,7 @@ STMT(CXXConditionDeclExpr , DeclRefExpr) STMT(CXXNewExpr , Expr) STMT(CXXDeleteExpr , Expr) STMT(CXXDependentNameExpr , Expr) +STMT(UnaryTypeTraitExpr , Expr) // Obj-C Expressions. STMT(ObjCStringLiteral , Expr) diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 5a6a3c361c..afcd1edd80 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -295,7 +295,10 @@ public: bool isIncompleteOrObjectType() const { return !isFunctionType(); } - + + /// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10). + bool isPODType() const; + /// isVariablyModifiedType (C99 6.7.5.2p2) - Return true for variable array /// types that have a non-constant expression. This does not include "[]". bool isVariablyModifiedType() const; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index 01a85c0a18..5cb4622f14 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -304,6 +304,25 @@ KEYWORD(__label__ , EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__real , EXTC90|EXTC99|EXTCPP|EXTCPP0x) KEYWORD(__thread , EXTC90|EXTC99|EXTCPP|EXTCPP0x) +// GNU and MS Type Traits +KEYWORD(__has_nothrow_assign , NOTC90|NOTC99) +KEYWORD(__has_nothrow_copy , NOTC90|NOTC99) +KEYWORD(__has_nothrow_constructor , NOTC90|NOTC99) +KEYWORD(__has_trivial_assign , NOTC90|NOTC99) +KEYWORD(__has_trivial_copy , NOTC90|NOTC99) +KEYWORD(__has_trivial_constructor , NOTC90|NOTC99) +KEYWORD(__has_trivial_destructor , NOTC90|NOTC99) +KEYWORD(__has_virtual_destructor , NOTC90|NOTC99) +KEYWORD(__is_abstract , NOTC90|NOTC99) +KEYWORD(__is_base_of , NOTC90|NOTC99) +KEYWORD(__is_class , NOTC90|NOTC99) +KEYWORD(__is_empty , NOTC90|NOTC99) +KEYWORD(__is_enum , NOTC90|NOTC99) +KEYWORD(__is_pod , NOTC90|NOTC99) +KEYWORD(__is_polymorphic , NOTC90|NOTC99) +KEYWORD(__is_union , NOTC90|NOTC99) +// FIXME: Add MS's traits, too. + // Apple Extension. KEYWORD(__private_extern__ , EXTC90|EXTC99|NOTCPP) diff --git a/include/clang/Basic/TypeTraits.h b/include/clang/Basic/TypeTraits.h new file mode 100644 index 0000000000..2a2eacc4ca --- /dev/null +++ b/include/clang/Basic/TypeTraits.h @@ -0,0 +1,40 @@ +//===--- TypeTraits.h - C++ Type Traits Support Enumerations ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines enumerations for the type traits support. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TYPETRAITS_H +#define LLVM_CLANG_TYPETRAITS_H + +namespace clang { + + /// UnaryTypeTrait - Names for the unary type traits. + enum UnaryTypeTrait { + UTT_HasNothrowAssign, + UTT_HasNothrowCopy, + UTT_HasNothrowConstructor, + UTT_HasTrivialAssign, + UTT_HasTrivialCopy, + UTT_HasTrivialConstructor, + UTT_HasTrivialDestructor, + UTT_HasVirtualDestructor, + UTT_IsAbstract, + UTT_IsClass, + UTT_IsEmpty, + UTT_IsEnum, + UTT_IsPOD, + UTT_IsPolymorphic, + UTT_IsUnion + }; + +} + +#endif diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 096104b915..5bba2a7b6d 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -16,6 +16,7 @@ #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TypeTraits.h" #include "clang/Parse/AccessSpecifier.h" #include "clang/Parse/Ownership.h" @@ -892,6 +893,14 @@ public: return 0; } + virtual OwningExprResult ActOnUnaryTypeTrait(UnaryTypeTrait OTT, + SourceLocation KWLoc, + SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen) { + return ExprEmpty(); + } + //===---------------------------- C++ Classes ---------------------------===// /// ActOnBaseSpecifier - Parsed a base specifier virtual BaseResult ActOnBaseSpecifier(DeclTy *classdecl, diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 070faa16bb..389d2b390b 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -984,7 +984,10 @@ private: void AnnotateTemplateIdToken(DeclTy *Template, const CXXScopeSpec *SS = 0); bool ParseTemplateArgumentList(TemplateArgList &TemplateArgs); OwningTemplateArgResult ParseTemplateArgument(); - + + //===--------------------------------------------------------------------===// + // GNU G++: Type Traits [Type-Traits.html in the GCC manual] + OwningExprResult ParseUnaryTypeTrait(); }; } // end namespace clang diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 66afac8524..545f18ed05 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -56,9 +56,9 @@ CXXRecordDecl::CXXRecordDecl(TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id) : RecordDecl(CXXRecord, TK, DC, L, Id), UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false), - UserDeclaredDestructor(false), Aggregate(true), Polymorphic(false), - Bases(0), NumBases(0), - Conversions(DC, DeclarationName()) { } + UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false), + Aggregate(true), PlainOldData(true), Polymorphic(false), Bases(0), + NumBases(0), Conversions(DC, DeclarationName()) { } CXXRecordDecl *CXXRecordDecl::Create(ASTContext &C, TagKind TK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id, @@ -91,7 +91,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, } bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const { - QualType ClassType = Context.getTypeDeclType(const_cast(this)); + QualType ClassType + = Context.getTypeDeclType(const_cast(this)); DeclarationName ConstructorName = Context.DeclarationNames.getCXXConstructorName( Context.getCanonicalType(ClassType)); @@ -107,7 +108,49 @@ bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const { return false; } -void +bool CXXRecordDecl::hasConstCopyAssignment(ASTContext &Context) const { + QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType( + const_cast(this))); + DeclarationName OpName =Context.DeclarationNames.getCXXOperatorName(OO_Equal); + + DeclContext::lookup_const_iterator Op, OpEnd; + for (llvm::tie(Op, OpEnd) = this->lookup(Context, OpName); + Op != OpEnd; ++Op) { + // C++ [class.copy]p9: + // A user-declared copy assignment operator is a non-static non-template + // member function of class X with exactly one parameter of type X, X&, + // const X&, volatile X& or const volatile X&. + const CXXMethodDecl* Method = cast(*Op); + if (Method->isStatic()) + continue; + // TODO: Skip templates? Or is this implicitly done due to parameter types? + const FunctionTypeProto *FnType = + Method->getType()->getAsFunctionTypeProto(); + assert(FnType && "Overloaded operator has no prototype."); + // Don't assert on this; an invalid decl might have been left in the AST. + if (FnType->getNumArgs() != 1 || FnType->isVariadic()) + continue; + bool AcceptsConst = true; + QualType ArgType = FnType->getArgType(0); + if (const ReferenceType *Ref = ArgType->getAsReferenceType()) { + ArgType = Ref->getPointeeType(); + // Is it a non-const reference? + if (!ArgType.isConstQualified()) + AcceptsConst = false; + } + if (Context.getCanonicalType(ArgType).getUnqualifiedType() != ClassType) + continue; + + // We have a single argument of type cv X or cv X&, i.e. we've found the + // copy assignment operator. Return whether it accepts const arguments. + return AcceptsConst; + } + assert(isInvalidDecl() && + "No copy assignment operator declared in valid code."); + return false; +} + +void CXXRecordDecl::addedConstructor(ASTContext &Context, CXXConstructorDecl *ConDecl) { if (!ConDecl->isImplicitlyDeclared()) { @@ -119,6 +162,10 @@ CXXRecordDecl::addedConstructor(ASTContext &Context, // user-declared constructors (12.1) [...]. Aggregate = false; + // C++ [class]p4: + // A POD-struct is an aggregate class [...] + PlainOldData = false; + // Note when we have a user-declared copy constructor, which will // suppress the implicit declaration of a copy constructor. if (ConDecl->isCopyConstructor(Context)) @@ -126,6 +173,35 @@ CXXRecordDecl::addedConstructor(ASTContext &Context, } } +void CXXRecordDecl::addedAssignmentOperator(ASTContext &Context, + CXXMethodDecl *OpDecl) { + // We're interested specifically in copy assignment operators. + // Unlike addedConstructor, this method is not called for implicit + // declarations. + const FunctionTypeProto *FnType = OpDecl->getType()->getAsFunctionTypeProto(); + assert(FnType && "Overloaded operator has no proto function type."); + assert(FnType->getNumArgs() == 1 && !FnType->isVariadic()); + QualType ArgType = FnType->getArgType(0); + if (const ReferenceType *Ref = ArgType->getAsReferenceType()) + ArgType = Ref->getPointeeType(); + + ArgType = ArgType.getUnqualifiedType(); + QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType( + const_cast(this))); + + if (ClassType != Context.getCanonicalType(ArgType)) + return; + + // This is a copy assignment operator. + // Suppress the implicit declaration of a copy constructor. + UserDeclaredCopyAssignment = true; + + // C++ [class]p4: + // A POD-struct is an aggregate class that [...] has no user-defined copy + // assignment operator [...]. + PlainOldData = false; +} + void CXXRecordDecl::addConversionFunction(ASTContext &Context, CXXConversionDecl *ConvDecl) { Conversions.addOverload(ConvDecl); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 6cfcdc4914..eafa717d65 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -1092,6 +1092,10 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, case CXXDefaultArgExprClass: return cast(this) ->isIntegerConstantExpr(Result, Ctx, Loc, isEvaluated); + + case UnaryTypeTraitExprClass: + Result = cast(this)->Evaluate(); + return true; } // Cases that are valid constant exprs fall through to here. diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 4cc5c741cf..07d794ea43 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -120,6 +120,35 @@ Stmt::child_iterator CXXDependentNameExpr::child_end() { return child_iterator(); } +// UnaryTypeTraitExpr +Stmt::child_iterator UnaryTypeTraitExpr::child_begin() { + return child_iterator(); +} +Stmt::child_iterator UnaryTypeTraitExpr::child_end() { + return child_iterator(); +} + +bool UnaryTypeTraitExpr::Evaluate() const { + switch(UTT) { + default: assert(false && "Unknown type trait or not implemented"); + case UTT_IsPOD: return QueriedType->isPODType(); + case UTT_IsClass: // Fallthrough + case UTT_IsUnion: + if (const RecordType *Record = QueriedType->getAsRecordType()) { + bool Union = Record->getDecl()->isUnion(); + return UTT == UTT_IsUnion ? Union : !Union; + } + return false; + case UTT_IsEnum: return QueriedType->isEnumeralType(); + case UTT_IsPolymorphic: + if (const RecordType *Record = QueriedType->getAsRecordType()) { + // Type traits are only parsed in C++, so we've got CXXRecords. + return cast(Record->getDecl())->isPolymorphic(); + } + return false; + } +} + OverloadedOperatorKind CXXOperatorCallExpr::getOperator() const { // All simple function calls (e.g. func()) are implicitly cast to pointer to // function. As a result, we try and obtain the DeclRefExpr from the diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index db9536633b..de6a14b87b 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -428,6 +428,12 @@ public: return true; } + bool VisitUnaryTypeTraitExpr(const UnaryTypeTraitExpr *E) { + Result.zextOrTrunc(getIntTypeSizeInBits(E->getType())); + Result = E->Evaluate(); + return true; + } + private: bool HandleCast(CastExpr* E); }; diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 521478953f..89a185d965 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -1020,6 +1020,32 @@ void StmtPrinter::VisitCXXDependentNameExpr(CXXDependentNameExpr *E) { OS << E->getName()->getName(); } +static const char *getTypeTraitName(UnaryTypeTrait UTT) { + switch (UTT) { + default: assert(false && "Unknown type trait"); + case UTT_HasNothrowAssign: return "__has_nothrow_assign"; + case UTT_HasNothrowCopy: return "__has_nothrow_copy"; + case UTT_HasNothrowConstructor: return "__has_nothrow_constructor"; + case UTT_HasTrivialAssign: return "__has_trivial_assign"; + case UTT_HasTrivialCopy: return "__has_trivial_copy"; + case UTT_HasTrivialConstructor: return "__has_trivial_constructor"; + case UTT_HasTrivialDestructor: return "__has_trivial_destructor"; + case UTT_HasVirtualDestructor: return "__has_virtual_destructor"; + case UTT_IsAbstract: return "__is_abstract"; + case UTT_IsClass: return "__is_class"; + case UTT_IsEmpty: return "__is_empty"; + case UTT_IsEnum: return "__is_enum"; + case UTT_IsPOD: return "__is_pod"; + case UTT_IsPolymorphic: return "__is_polymorphic"; + case UTT_IsUnion: return "__is_union"; + } +} + +void StmtPrinter::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E) { + OS << getTypeTraitName(E->getTrait()) << "(" + << E->getQueriedType().getAsString() << ")"; +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { diff --git a/lib/AST/StmtSerialization.cpp b/lib/AST/StmtSerialization.cpp index 3f7c0384d7..f7c4cf9b93 100644 --- a/lib/AST/StmtSerialization.cpp +++ b/lib/AST/StmtSerialization.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Basic/TypeTraits.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" @@ -1530,6 +1531,24 @@ CXXDependentNameExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { return new CXXDependentNameExpr(N, Ty, L); } +void UnaryTypeTraitExpr::EmitImpl(llvm::Serializer& S) const { + S.EmitInt(UTT); + S.Emit(Loc); + S.Emit(RParen); + S.Emit(QueriedType); + S.Emit(getType()); +} + +UnaryTypeTraitExpr * +UnaryTypeTraitExpr::CreateImpl(llvm::Deserializer& D, ASTContext& C) { + UnaryTypeTrait UTT = static_cast(D.ReadInt()); + SourceLocation Loc = SourceLocation::ReadVal(D); + SourceLocation RParen = SourceLocation::ReadVal(D); + QualType QueriedType = QualType::ReadVal(D); + QualType Ty = QualType::ReadVal(D); + return new UnaryTypeTraitExpr(Loc, UTT, QueriedType, RParen, Ty); +} + void CXXCatchStmt::EmitImpl(llvm::Serializer& S) const { S.Emit(CatchLoc); S.EmitOwnedPtr(ExceptionDecl); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index c70ad4af52..1d42b64bb1 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -670,6 +670,42 @@ bool Type::isIncompleteType() const { } } +/// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10) +bool Type::isPODType() const { + // The compiler shouldn't query this for incomplete types, but the user might. + // We return false for that case. + if (isIncompleteType()) + return false; + + switch (CanonicalType->getTypeClass()) { + // Everything not explicitly mentioned is not POD. + default: return false; + case ASQual: + return cast(CanonicalType)->getBaseType()->isPODType(); + case VariableArray: + case ConstantArray: + // IncompleteArray is caught by isIncompleteType() above. + return cast(CanonicalType)->getElementType()->isPODType(); + + case Builtin: + case Complex: + case Pointer: + case Vector: + case ExtVector: + // FIXME: pointer-to-member + return true; + + case Tagged: + if (isEnumeralType()) + return true; + if (CXXRecordDecl *RDecl = dyn_cast( + cast(CanonicalType)->getDecl())) + return RDecl->isPOD(); + // C struct/union is POD. + return true; + } +} + bool Type::isPromotableIntegerType() const { if (const ASQualType *ASQT = dyn_cast(CanonicalType)) return ASQT->getBaseType()->isPromotableIntegerType(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index c2bbad3cda..94e686b6d7 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -381,6 +381,8 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { /// [C++] 'typeid' '(' expression ')' [C++ 5.2p1] /// [C++] 'typeid' '(' type-id ')' [C++ 5.2p1] /// [C++] 'this' [C++ 9.3.2] +/// [G++] unary-type-trait '(' type-id ')' +/// [G++] binary-type-trait '(' type-id ',' type-id ')' [TODO] /// [clang] '^' block-literal /// /// constant: [C99 6.4.4] @@ -410,6 +412,26 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, unsigned MinPrec) { /// '::'[opt] 'delete' cast-expression /// '::'[opt] 'delete' '[' ']' cast-expression /// +/// [GNU] unary-type-trait: +/// '__has_nothrow_assign' [TODO] +/// '__has_nothrow_copy' [TODO] +/// '__has_nothrow_constructor' [TODO] +/// '__has_trivial_assign' [TODO] +/// '__has_trivial_copy' [TODO] +/// '__has_trivial_constructor' [TODO] +/// '__has_trivial_destructor' [TODO] +/// '__has_virtual_destructor' [TODO] +/// '__is_abstract' [TODO] +/// '__is_class' +/// '__is_empty' [TODO] +/// '__is_enum' +/// '__is_pod' +/// '__is_polymorphic' +/// '__is_union' +/// +/// [GNU] binary-type-trait: +/// '__is_base_of' [TODO] +/// Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression) { OwningExprResult Res(Actions); tok::TokenKind SavedKind = Tok.getKind(); @@ -650,6 +672,13 @@ Parser::OwningExprResult Parser::ParseCastExpression(bool isUnaryExpression) { case tok::kw_delete: // [C++] delete-expression return ParseCXXDeleteExpression(false, Tok.getLocation()); + case tok::kw___is_pod: // [GNU] unary-type-trait + case tok::kw___is_class: + case tok::kw___is_enum: + case tok::kw___is_union: + case tok::kw___is_polymorphic: + return ParseUnaryTypeTrait(); + case tok::at: { SourceLocation AtLoc = ConsumeToken(); return ParseObjCAtExpression(AtLoc); diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index c7c6be9152..5731f1f1c6 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -839,3 +839,51 @@ Parser::ParseCXXDeleteExpression(bool UseGlobal, SourceLocation Start) { return Owned(Actions.ActOnCXXDelete(Start, UseGlobal, ArrayDelete, Operand.release())); } + +static UnaryTypeTrait UnaryTypeTraitFromTokKind(tok::TokenKind kind) +{ + switch(kind) { + default: assert(false && "Not a known unary type trait."); + case tok::kw___has_nothrow_assign: return UTT_HasNothrowAssign; + case tok::kw___has_nothrow_copy: return UTT_HasNothrowCopy; + case tok::kw___has_nothrow_constructor: return UTT_HasNothrowConstructor; + case tok::kw___has_trivial_assign: return UTT_HasTrivialAssign; + case tok::kw___has_trivial_copy: return UTT_HasTrivialCopy; + case tok::kw___has_trivial_constructor: return UTT_HasTrivialConstructor; + case tok::kw___has_trivial_destructor: return UTT_HasTrivialDestructor; + case tok::kw___has_virtual_destructor: return UTT_HasVirtualDestructor; + case tok::kw___is_abstract: return UTT_IsAbstract; + case tok::kw___is_class: return UTT_IsClass; + case tok::kw___is_empty: return UTT_IsEmpty; + case tok::kw___is_enum: return UTT_IsEnum; + case tok::kw___is_pod: return UTT_IsPOD; + case tok::kw___is_polymorphic: return UTT_IsPolymorphic; + case tok::kw___is_union: return UTT_IsUnion; + } +} + +/// ParseUnaryTypeTrait - Parse the built-in unary type-trait +/// pseudo-functions that allow implementation of the TR1/C++0x type traits +/// templates. +/// +/// primary-expression: +/// [GNU] unary-type-trait '(' type-id ')' +/// +Parser::OwningExprResult Parser::ParseUnaryTypeTrait() +{ + UnaryTypeTrait UTT = UnaryTypeTraitFromTokKind(Tok.getKind()); + SourceLocation Loc = ConsumeToken(); + + SourceLocation LParen = Tok.getLocation(); + if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen)) + return ExprError(); + + // FIXME: Error reporting absolutely sucks! If the this fails to parse a type + // there will be cryptic errors about mismatched parentheses and missing + // specifiers. + TypeTy *Ty = ParseTypeName(); + + SourceLocation RParen = MatchRHSPunctuation(tok::r_paren, LParen); + + return Actions.ActOnUnaryTypeTrait(UTT, Loc, LParen, Ty, RParen); +} diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index d3f373f494..e35c06dd2f 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -918,6 +918,14 @@ public: SourceLocation EqualLoc, ExprTy *AssignExprVal); + /// ActOnUnaryTypeTrait - Parsed one of the unary type trait support + /// pseudo-functions. + virtual OwningExprResult ActOnUnaryTypeTrait(UnaryTypeTrait OTT, + SourceLocation KWLoc, + SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen); + /// ActOnCXXGlobalScopeSpecifier - Return the object that represents the /// global scope ('::'). virtual CXXScopeTy *ActOnCXXGlobalScopeSpecifier(Scope *S, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index f4677f03f4..0396f520a8 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1153,7 +1153,7 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, FunctionDecl::StorageClass SC = FunctionDecl::None; switch (D.getDeclSpec().getStorageClassSpec()) { default: assert(0 && "Unknown storage class!"); - case DeclSpec::SCS_auto: + case DeclSpec::SCS_auto: case DeclSpec::SCS_register: case DeclSpec::SCS_mutable: Diag(D.getIdentifierLoc(), diag::err_typecheck_sclass_func); @@ -1319,9 +1319,14 @@ Sema::ActOnDeclarator(Scope *S, Declarator &D, DeclTy *lastDecl, if (CXXConstructorDecl *Constructor = dyn_cast(NewFD)) InvalidDecl = InvalidDecl || CheckConstructor(Constructor); - else if (isa(NewFD)) - cast(NewFD->getParent())->setUserDeclaredDestructor(true); - else if (CXXConversionDecl *Conversion = dyn_cast(NewFD)) + else if (isa(NewFD)) { + CXXRecordDecl *Record = cast(NewFD->getParent()); + Record->setUserDeclaredDestructor(true); + // C++ [class]p4: A POD-struct is an aggregate class that has [...] no + // user-defined destructor. + Record->setPOD(false); + } else if (CXXConversionDecl *Conversion = + dyn_cast(NewFD)) ActOnConversionDeclarator(Conversion); // Extra checking for C++ overloaded operators (C++ [over.oper]). @@ -2836,7 +2841,7 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD, QualType T = GetTypeForDeclarator(D, S); assert(!T.isNull() && "GetTypeForDeclarator() returned null type"); bool InvalidDecl = false; - + // C99 6.7.2.1p8: A member of a structure or union may have any type other // than a variably modified type. if (T->isVariablyModifiedType()) { @@ -2872,8 +2877,11 @@ Sema::DeclTy *Sema::ActOnField(Scope *S, DeclTy *TagD, DeclSpec::SCS_mutable, /*PrevDecl=*/0); - if (getLangOptions().CPlusPlus) + if (getLangOptions().CPlusPlus) { CheckExtraCXXDefaultArguments(D); + if (!T->isPODType()) + cast(Record)->setPOD(false); + } ProcessDeclAttributes(NewFD, D); @@ -3088,7 +3096,7 @@ void Sema::ActOnFields(Scope* S, ++NumNamedMembers; } } - + // Okay, we successfully defined 'Record'. if (Record) { Record->completeDefinition(Context); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 7c05f6b497..e67b8c0389 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -317,7 +317,7 @@ Sema::BaseResult Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, bool Virtual, AccessSpecifier Access, TypeTy *basetype, SourceLocation BaseLoc) { - RecordDecl *Decl = (RecordDecl*)classdecl; + CXXRecordDecl *Decl = (CXXRecordDecl*)classdecl; QualType BaseType = Context.getTypeDeclType((TypeDecl*)basetype); // Base specifiers must be record types. @@ -347,7 +347,12 @@ Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange, BaseDecl = BaseDecl->getDefinition(Context); assert(BaseDecl && "Base type is not incomplete, but has no definition"); if (cast(BaseDecl)->isPolymorphic()) - cast(Decl)->setPolymorphic(true); + Decl->setPolymorphic(true); + + // C++ [dcl.init.aggr]p1: + // An aggregate is [...] a class with [...] no base classes [...]. + Decl->setAggregate(false); + Decl->setPOD(false); // Create the base specifier. return new CXXBaseSpecifier(SpecifierRange, Virtual, @@ -537,8 +542,12 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class (clause 9) with [...] no // private or protected non-static data members (clause 11). - if (isInstField && (AS == AS_private || AS == AS_protected)) - cast(CurContext)->setAggregate(false); + // A POD must be an aggregate. + if (isInstField && (AS == AS_private || AS == AS_protected)) { + CXXRecordDecl *Record = cast(CurContext); + Record->setAggregate(false); + Record->setPOD(false); + } if (DS.isVirtualSpecified()) { if (!isFunc || DS.getStorageClassSpec() == DeclSpec::SCS_static) { @@ -547,6 +556,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, } else { CXXRecordDecl *CurClass = cast(CurContext); CurClass->setAggregate(false); + CurClass->setPOD(false); CurClass->setPolymorphic(true); } } @@ -827,17 +837,17 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { } } - // Otherwise, the implicitly declared copy constructor will have - // the form + // Otherwise, the implicitly declared copy constructor will have + // the form // // X::X(X&) - QualType ArgType = Context.getTypeDeclType(ClassDecl); + QualType ArgType = ClassType; if (HasConstCopyConstructor) ArgType = ArgType.withConst(); ArgType = Context.getReferenceType(ArgType); - // An implicitly-declared copy constructor is an inline public - // member of its class. + // An implicitly-declared copy constructor is an inline public + // member of its class. DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(ClassType); CXXConstructorDecl *CopyConstructor @@ -862,6 +872,83 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { ClassDecl->addDecl(Context, CopyConstructor); } + if (!ClassDecl->hasUserDeclaredCopyAssignment()) { + // Note: The following rules are largely analoguous to the copy + // constructor rules. Note that virtual bases are not taken into account + // for determining the argument type of the operator. Note also that + // operators taking an object instead of a reference are allowed. + // + // C++ [class.copy]p10: + // If the class definition does not explicitly declare a copy + // assignment operator, one is declared implicitly. + // The implicitly-defined copy assignment operator for a class X + // will have the form + // + // X& X::operator=(const X&) + // + // if + bool HasConstCopyAssignment = true; + + // -- each direct base class B of X has a copy assignment operator + // whose parameter is of type const B&, const volatile B& or B, + // and + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(); + HasConstCopyAssignment && Base != ClassDecl->bases_end(); ++Base) { + const CXXRecordDecl *BaseClassDecl + = cast(Base->getType()->getAsRecordType()->getDecl()); + HasConstCopyAssignment = BaseClassDecl->hasConstCopyAssignment(Context); + } + + // -- for all the nonstatic data members of X that are of a class + // type M (or array thereof), each such class type has a copy + // assignment operator whose parameter is of type const M&, + // const volatile M& or M. + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(); + HasConstCopyAssignment && Field != ClassDecl->field_end(); ++Field) { + QualType FieldType = (*Field)->getType(); + if (const ArrayType *Array = Context.getAsArrayType(FieldType)) + FieldType = Array->getElementType(); + if (const RecordType *FieldClassType = FieldType->getAsRecordType()) { + const CXXRecordDecl *FieldClassDecl + = cast(FieldClassType->getDecl()); + HasConstCopyAssignment + = FieldClassDecl->hasConstCopyAssignment(Context); + } + } + + // Otherwise, the implicitly declared copy assignment operator will + // have the form + // + // X& X::operator=(X&) + QualType ArgType = ClassType; + QualType RetType = Context.getReferenceType(ArgType); + if (HasConstCopyAssignment) + ArgType = ArgType.withConst(); + ArgType = Context.getReferenceType(ArgType); + + // An implicitly-declared copy assignment operator is an inline public + // member of its class. + DeclarationName Name = + Context.DeclarationNames.getCXXOperatorName(OO_Equal); + CXXMethodDecl *CopyAssignment = + CXXMethodDecl::Create(Context, ClassDecl, ClassDecl->getLocation(), Name, + Context.getFunctionType(RetType, &ArgType, 1, + false, 0), + /*isStatic=*/false, /*isInline=*/true, 0); + CopyAssignment->setAccess(AS_public); + + // Add the parameter to the operator. + ParmVarDecl *FromParam = ParmVarDecl::Create(Context, CopyAssignment, + ClassDecl->getLocation(), + /*IdentifierInfo=*/0, + ArgType, VarDecl::None, 0, 0); + CopyAssignment->setParams(&FromParam, 1); + + // Don't call addedAssignmentOperator. There is no way to distinguish an + // implicit from an explicit assignment operator. + ClassDecl->addDecl(Context, CopyAssignment); + } + if (!ClassDecl->hasUserDeclaredDestructor()) { // C++ [class.dtor]p2: // If a class has no user-declared destructor, a destructor is @@ -879,8 +966,6 @@ void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { Destructor->setAccess(AS_public); ClassDecl->addDecl(Context, Destructor); } - - // FIXME: Implicit copy assignment operator } void Sema::ActOnFinishCXXClassDef(DeclTy *D) { @@ -1963,7 +2048,7 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be) << FnDecl->getDeclName() << NumParams << ErrorKind; } - + // Overloaded operators other than operator() cannot be variadic. if (Op != OO_Call && FnDecl->getType()->getAsFunctionTypeProto()->isVariadic()) { @@ -2000,6 +2085,15 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { << LastParam->getType() << (Op == OO_MinusMinus); } + // Notify the class if it got an assignment operator. + if (Op == OO_Equal) { + // Would have returned earlier otherwise. + assert(isa(FnDecl) && + "Overloaded = not member, but not filtered."); + CXXMethodDecl *Method = cast(FnDecl); + Method->getParent()->addedAssignmentOperator(Context, Method); + } + return false; } diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index d367a91aa5..3510b5d128 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -842,3 +842,20 @@ Sema::PerformImplicitConversion(Expr *&From, QualType ToType, return false; } +Sema::OwningExprResult Sema::ActOnUnaryTypeTrait(UnaryTypeTrait OTT, + SourceLocation KWLoc, + SourceLocation LParen, + TypeTy *Ty, + SourceLocation RParen) { + // FIXME: Some of the type traits have requirements. Interestingly, only the + // __is_base_of requirement is explicitly stated to be diagnosed. Indeed, + // G++ accepts __is_pod(Incomplete) without complaints, and claims that the + // type is indeed a POD. + + // There is no point in eagerly computing the value. The traits are designed + // to be used from type trait templates, so Ty will be a template parameter + // 99% of the time. + return Owned(new UnaryTypeTraitExpr(KWLoc, OTT, + QualType::getFromOpaquePtr(Ty), + RParen, Context.BoolTy)); +} diff --git a/test/SemaCXX/type-traits.cpp b/test/SemaCXX/type-traits.cpp new file mode 100644 index 0000000000..d008b01c34 --- /dev/null +++ b/test/SemaCXX/type-traits.cpp @@ -0,0 +1,111 @@ +// RUN: clang -fsyntax-only -verify %s +#define T(b) (b) ? 1 : -1 +#define F(b) (b) ? -1 : 1 + +struct NonPOD { NonPOD(int); }; + +// PODs +enum Enum { EV }; +struct POD { Enum e; int i; float f; NonPOD* p; }; +typedef int Int; +typedef Int IntAr[10]; +class Statics { static int priv; static NonPOD np; }; + +// Not PODs +struct Derives : POD {}; +struct HasCons { HasCons(int); }; +struct HasAssign { HasAssign operator =(const HasAssign&); }; +struct HasDest { ~HasDest(); }; +class HasPriv { int priv; }; +class HasProt { protected: int prot; }; +struct HasRef { int i; int& ref; HasRef() : i(0), ref(i) {} }; +struct HasNonPOD { NonPOD np; }; +struct HasVirt { virtual void Virt() {}; }; +typedef Derives NonPODAr[10]; + +void is_pod() +{ + int t01[T(__is_pod(int))]; + int t02[T(__is_pod(Enum))]; + int t03[T(__is_pod(POD))]; + int t04[T(__is_pod(Int))]; + int t05[T(__is_pod(IntAr))]; + int t06[T(__is_pod(Statics))]; + + int t21[F(__is_pod(Derives))]; + int t22[F(__is_pod(HasCons))]; + int t23[F(__is_pod(HasAssign))]; + int t24[F(__is_pod(HasDest))]; + int t25[F(__is_pod(HasPriv))]; + int t26[F(__is_pod(HasProt))]; + int t27[F(__is_pod(HasRef))]; + int t28[F(__is_pod(HasNonPOD))]; + int t29[F(__is_pod(HasVirt))]; + int t30[F(__is_pod(NonPODAr))]; +} + +union Union { int i; float f; }; +typedef Derives ClassType; + +void is_class() +{ + int t01[T(__is_class(Derives))]; + int t02[T(__is_class(HasPriv))]; + int t03[T(__is_class(ClassType))]; + + int t11[F(__is_class(int))]; + int t12[F(__is_class(Enum))]; + int t13[F(__is_class(Int))]; + int t14[F(__is_class(IntAr))]; + int t15[F(__is_class(NonPODAr))]; + int t16[F(__is_class(Union))]; +} + +typedef Union UnionAr[10]; +typedef Union UnionType; + +void is_union() +{ + int t01[T(__is_union(Union))]; + int t02[T(__is_union(UnionType))]; + + int t11[F(__is_union(int))]; + int t12[F(__is_union(Enum))]; + int t13[F(__is_union(Int))]; + int t14[F(__is_union(IntAr))]; + int t15[F(__is_union(UnionAr))]; +} + +typedef Enum EnumType; + +void is_enum() +{ + int t01[T(__is_enum(Enum))]; + int t02[T(__is_enum(EnumType))]; + + int t11[F(__is_enum(int))]; + int t12[F(__is_enum(Union))]; + int t13[F(__is_enum(Int))]; + int t14[F(__is_enum(IntAr))]; + int t15[F(__is_enum(UnionAr))]; + int t16[F(__is_enum(Derives))]; + int t17[F(__is_enum(ClassType))]; +} + +struct Polymorph { virtual void f(); }; +struct InheritPolymorph : Polymorph {}; + +void is_polymorphic() +{ + int t01[T(__is_polymorphic(Polymorph))]; + int t02[T(__is_polymorphic(InheritPolymorph))]; + + int t11[F(__is_polymorphic(int))]; + int t12[F(__is_polymorphic(Union))]; + int t13[F(__is_polymorphic(Int))]; + int t14[F(__is_polymorphic(IntAr))]; + int t15[F(__is_polymorphic(UnionAr))]; + int t16[F(__is_polymorphic(Derives))]; + int t17[F(__is_polymorphic(ClassType))]; + int t18[F(__is_polymorphic(Enum))]; +}