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/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index 66afac8..545f18e 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -56,9 +56,9 @@
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 @@
}
bool CXXRecordDecl::hasConstCopyConstructor(ASTContext &Context) const {
- QualType ClassType = Context.getTypeDeclType(const_cast<CXXRecordDecl*>(this));
+ QualType ClassType
+ = Context.getTypeDeclType(const_cast<CXXRecordDecl*>(this));
DeclarationName ConstructorName
= Context.DeclarationNames.getCXXConstructorName(
Context.getCanonicalType(ClassType));
@@ -107,7 +108,49 @@
return false;
}
-void
+bool CXXRecordDecl::hasConstCopyAssignment(ASTContext &Context) const {
+ QualType ClassType = Context.getCanonicalType(Context.getTypeDeclType(
+ const_cast<CXXRecordDecl*>(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<CXXMethodDecl>(*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 @@
// 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 @@
}
}
+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<CXXRecordDecl*>(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 6cfcdc4..eafa717 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -1092,6 +1092,10 @@
case CXXDefaultArgExprClass:
return cast<CXXDefaultArgExpr>(this)
->isIntegerConstantExpr(Result, Ctx, Loc, isEvaluated);
+
+ case UnaryTypeTraitExprClass:
+ Result = cast<UnaryTypeTraitExpr>(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 4cc5c74..07d794e 100644
--- a/lib/AST/ExprCXX.cpp
+++ b/lib/AST/ExprCXX.cpp
@@ -120,6 +120,35 @@
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<CXXRecordDecl>(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 db95366..de6a14b 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -428,6 +428,12 @@
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 5214789..89a185d 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -1020,6 +1020,32 @@
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 3f7c038..f7c4cf9 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 @@
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<UnaryTypeTrait>(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 c70ad4a..1d42b64 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -670,6 +670,42 @@
}
}
+/// 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<ASQualType>(CanonicalType)->getBaseType()->isPODType();
+ case VariableArray:
+ case ConstantArray:
+ // IncompleteArray is caught by isIncompleteType() above.
+ return cast<ArrayType>(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<CXXRecordDecl>(
+ cast<TagType>(CanonicalType)->getDecl()))
+ return RDecl->isPOD();
+ // C struct/union is POD.
+ return true;
+ }
+}
+
bool Type::isPromotableIntegerType() const {
if (const ASQualType *ASQT = dyn_cast<ASQualType>(CanonicalType))
return ASQT->getBaseType()->isPromotableIntegerType();