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));
+}