]> granicus.if.org Git - clang/commitdiff
PODness and Type Traits
authorSebastian Redl <sebastian.redl@getdesigned.at>
Mon, 5 Jan 2009 20:52:13 +0000 (20:52 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Mon, 5 Jan 2009 20:52:13 +0000 (20:52 +0000)
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

22 files changed:
include/clang/AST/DeclCXX.h
include/clang/AST/ExprCXX.h
include/clang/AST/StmtNodes.def
include/clang/AST/Type.h
include/clang/Basic/TokenKinds.def
include/clang/Basic/TypeTraits.h [new file with mode: 0644]
include/clang/Parse/Action.h
include/clang/Parse/Parser.h
lib/AST/DeclCXX.cpp
lib/AST/Expr.cpp
lib/AST/ExprCXX.cpp
lib/AST/ExprConstant.cpp
lib/AST/StmtPrinter.cpp
lib/AST/StmtSerialization.cpp
lib/AST/Type.cpp
lib/Parse/ParseExpr.cpp
lib/Parse/ParseExprCXX.cpp
lib/Sema/Sema.h
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExprCXX.cpp
test/SemaCXX/type-traits.cpp [new file with mode: 0644]

index 6f156fa82706adb27b9d46313830fbb3e8998196..583f57854a2da009bc7f1e73ca1b383ee038c2de 100644 (file)
@@ -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; }
index be33eb1939d12fdd3e9ceb5990548dc8aae18627..37436575a2450f72fa467257671220d310b94d77 100644 (file)
@@ -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
index 27b7848d16ab94b623f90ab52bcdb7a42bdb2a4b..7bd378220b056685507e33377c5cc5bbb7fac7b2 100644 (file)
@@ -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)
index 5a6a3c361c7e803b936a3acc3a20257cbcee7966..afcd1edd80fe209f7622cbba6e19389750a8c9b3 100644 (file)
@@ -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;
index 01a85c0a183f008fe4d5c87801c03fb586292f15..5cb4622f147e52a5bdf85bb57aeb4025e89c524a 100644 (file)
@@ -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 (file)
index 0000000..2a2eacc
--- /dev/null
@@ -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
index 096104b9159ec6e903792cfbef51f2aeacd5589b..5bba2a7b6df2ee31f43b14bb5bad9501cc1c9ade 100644 (file)
@@ -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, 
index 070faa16bb3d45473d946c2d1ffbcb7dcbcf1e38..389d2b390b252d10f2f5327f31d68a4da093f401 100644 (file)
@@ -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
index 66afac8524c0d2a04140f9d6971fdf91e7826948..545f18ed056bec039f94b51bd20eaedf014a38ad 100644 (file)
@@ -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<CXXRecordDecl*>(this));
+  QualType ClassType
+    = Context.getTypeDeclType(const_cast<CXXRecordDecl*>(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<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 @@ 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<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);
index 6cfcdc4914443e9a9050c0320610482ce5d7a60d..eafa717d654b2f5618b26688f35e4a9995ebac49 100644 (file)
@@ -1092,6 +1092,10 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx,
   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.
index 4cc5c741cfb8ec655ae04781b4fc4274a4e2d069..07d794ea437d16e186e0333613020024526edcf2 100644 (file)
@@ -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<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 
index db9536633be3358fd611b11178bef3d438066d24..de6a14b87b6ed533d5b567cfc0f4a4814ba1ca82 100644 (file)
@@ -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);
 };
index 521478953f23a0305baaf313f291b02fdb3f9b46..89a185d965d566fe314cafe5907bf1c53349c3a9 100644 (file)
@@ -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) {
index 3f7c0384d739aab6acb4a7065b8fdba8591f1b74..f7c4cf9b937593242410929bf2731a003c03bf0d 100644 (file)
@@ -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<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);
index c70ad4af521ef34c2dc01a45423bbc96f058bf65..1d42b64bb12735fc957e47a3fa074b37190ac90e 100644 (file)
@@ -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<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();
index c2bbad3cda101d8b11687374488fea0e0ce740ea..94e686b6d7735f10c4cd23f7b10da87e85bd476d 100644 (file)
@@ -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);
index c7c6be9152651a873a5ceb697bc0571a11dbaf29..5731f1f1c6f836500d5bca0d4a7d6618e349a7b1 100644 (file)
@@ -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);
+}
index d3f373f49497e335b68e088993e6072081c1a30c..e35c06dd2f393d5508bb38b7f6455224c7fea6dd 100644 (file)
@@ -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,
index f4677f03f4115c6207ae9fbbaaf126c853e90b76..0396f520a8436b8fc989a14d9ff081b4291cc987 100644 (file)
@@ -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<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 @@ 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<CXXRecordDecl>(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);
index 7c05f6b49709a8a40d942da89c04199296207815..e67b8c0389706a4cece54a804ac0a9e0b4b2b25f 100644 (file)
@@ -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<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 @@ 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<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 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D,
     } else {
       CXXRecordDecl *CurClass = cast<CXXRecordDecl>(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<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 @@ 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<CXXMethodDecl>(FnDecl) &&
+      "Overloaded = not member, but not filtered.");
+    CXXMethodDecl *Method = cast<CXXMethodDecl>(FnDecl);
+    Method->getParent()->addedAssignmentOperator(Context, Method);
+  }
+
   return false;
 }
 
index d367a91aa5d9b29318209fdcf6582df1463d9d4a..3510b5d1289fc6a8714aa46e4fac16b7492a4fc3 100644 (file)
@@ -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 (file)
index 0000000..d008b01
--- /dev/null
@@ -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))];
+}