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/Sema/Sema.h b/lib/Sema/Sema.h
index d3f373f..e35c06d 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -918,6 +918,14 @@
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 f4677f0..0396f52 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -1153,7 +1153,7 @@
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 @@
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(NewFD))
InvalidDecl = InvalidDecl || CheckConstructor(Constructor);
- else if (isa<CXXDestructorDecl>(NewFD))
- cast<CXXRecordDecl>(NewFD->getParent())->setUserDeclaredDestructor(true);
- else if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
+ else if (isa<CXXDestructorDecl>(NewFD)) {
+ CXXRecordDecl *Record = cast<CXXRecordDecl>(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<CXXConversionDecl>(NewFD))
ActOnConversionDeclarator(Conversion);
// Extra checking for C++ overloaded operators (C++ [over.oper]).
@@ -2836,7 +2841,7 @@
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 @@
DeclSpec::SCS_mutable,
/*PrevDecl=*/0);
- if (getLangOptions().CPlusPlus)
+ if (getLangOptions().CPlusPlus) {
CheckExtraCXXDefaultArguments(D);
+ if (!T->isPODType())
+ cast<CXXRecordDecl>(Record)->setPOD(false);
+ }
ProcessDeclAttributes(NewFD, D);
@@ -3088,7 +3096,7 @@
++NumNamedMembers;
}
}
-
+
// Okay, we successfully defined 'Record'.
if (Record) {
Record->completeDefinition(Context);
diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp
index 7c05f6b..e67b8c0 100644
--- a/lib/Sema/SemaDeclCXX.cpp
+++ b/lib/Sema/SemaDeclCXX.cpp
@@ -317,7 +317,7 @@
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 @@
BaseDecl = BaseDecl->getDefinition(Context);
assert(BaseDecl && "Base type is not incomplete, but has no definition");
if (cast<CXXRecordDecl>(BaseDecl)->isPolymorphic())
- cast<CXXRecordDecl>(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 @@
// 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<CXXRecordDecl>(CurContext)->setAggregate(false);
+ // A POD must be an aggregate.
+ if (isInstField && (AS == AS_private || AS == AS_protected)) {
+ CXXRecordDecl *Record = cast<CXXRecordDecl>(CurContext);
+ Record->setAggregate(false);
+ Record->setPOD(false);
+ }
if (DS.isVirtualSpecified()) {
if (!isFunc || DS.getStorageClassSpec() == DeclSpec::SCS_static) {
@@ -547,6 +556,7 @@
} else {
CXXRecordDecl *CurClass = cast<CXXRecordDecl>(CurContext);
CurClass->setAggregate(false);
+ CurClass->setPOD(false);
CurClass->setPolymorphic(true);
}
}
@@ -827,17 +837,17 @@
}
}
- // 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 @@
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<CXXRecordDecl>(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<CXXRecordDecl>(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 @@
Destructor->setAccess(AS_public);
ClassDecl->addDecl(Context, Destructor);
}
-
- // FIXME: Implicit copy assignment operator
}
void Sema::ActOnFinishCXXClassDef(DeclTy *D) {
@@ -1963,7 +2048,7 @@
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 @@
<< 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<CXXMethodDecl>(FnDecl) &&
+ "Overloaded = not member, but not filtered.");
+ CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
+ Method->getParent()->addedAssignmentOperator(Context, Method);
+ }
+
return false;
}
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index d367a91..3510b5d 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -842,3 +842,20 @@
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));
+}