t/clang/type-traits
Patch authored by John Wiegley.
These type traits are used for parsing code that employs certain features of
the Embarcadero C++ compiler. Several of these constructs are also desired by
libc++, according to its project pages (such as __is_standard_layout).
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@130342 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp
index a71750b..7ab6030 100644
--- a/lib/AST/DeclCXX.cpp
+++ b/lib/AST/DeclCXX.cpp
@@ -926,6 +926,10 @@
void CXXRecordDecl::completeDefinition() {
completeDefinition(0);
+
+ ASTContext &Context = getASTContext();
+ if (const RecordType *RT = getTypeForDecl()->getAs<RecordType>())
+ data().HasStandardLayout = RT->hasStandardLayout(Context);
}
void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index cf42e63..b96591b 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -1251,23 +1251,48 @@
static const char *getTypeTraitName(UnaryTypeTrait UTT) {
switch (UTT) {
- default: llvm_unreachable("Unknown unary type trait");
+ 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_HasNothrowCopy: return "__has_nothrow_copy";
case UTT_HasTrivialAssign: return "__has_trivial_assign";
- case UTT_HasTrivialCopy: return "__has_trivial_copy";
case UTT_HasTrivialConstructor: return "__has_trivial_constructor";
+ case UTT_HasTrivialCopy: return "__has_trivial_copy";
case UTT_HasTrivialDestructor: return "__has_trivial_destructor";
case UTT_HasVirtualDestructor: return "__has_virtual_destructor";
case UTT_IsAbstract: return "__is_abstract";
+ case UTT_IsArithmetic: return "__is_arithmetic";
+ case UTT_IsArray: return "__is_array";
case UTT_IsClass: return "__is_class";
+ case UTT_IsCompleteType: return "__is_complete_type";
+ case UTT_IsCompound: return "__is_compound";
+ case UTT_IsConst: return "__is_const";
case UTT_IsEmpty: return "__is_empty";
case UTT_IsEnum: return "__is_enum";
+ case UTT_IsFloatingPoint: return "__is_floating_point";
+ case UTT_IsFunction: return "__is_function";
+ case UTT_IsFundamental: return "__is_fundamental";
+ case UTT_IsIntegral: return "__is_integral";
+ case UTT_IsLvalueExpr: return "__is_lvalue_expr";
+ case UTT_IsLvalueReference: return "__is_lvalue_reference";
+ case UTT_IsMemberFunctionPointer: return "__is_member_function_pointer";
+ case UTT_IsMemberObjectPointer: return "__is_member_object_pointer";
+ case UTT_IsMemberPointer: return "__is_member_pointer";
+ case UTT_IsObject: return "__is_object";
case UTT_IsPOD: return "__is_pod";
+ case UTT_IsPointer: return "__is_pointer";
case UTT_IsPolymorphic: return "__is_polymorphic";
- case UTT_IsTrivial: return "__is_trivial";
+ case UTT_IsReference: return "__is_reference";
+ case UTT_IsRvalueExpr: return "__is_rvalue_expr";
+ case UTT_IsRvalueReference: return "__is_rvalue_reference";
+ case UTT_IsScalar: return "__is_scalar";
+ case UTT_IsSigned: return "__is_signed";
+ case UTT_IsStandardLayout: return "__is_standard_layout";
+ case UTT_IsTrivial: return "__is_trivial";
case UTT_IsUnion: return "__is_union";
+ case UTT_IsUnsigned: return "__is_unsigned";
+ case UTT_IsVoid: return "__is_void";
+ case UTT_IsVolatile: return "__is_volatile";
}
return "";
}
@@ -1275,6 +1300,8 @@
static const char *getTypeTraitName(BinaryTypeTrait BTT) {
switch (BTT) {
case BTT_IsBaseOf: return "__is_base_of";
+ case BTT_IsConvertible: return "__is_convertible";
+ case BTT_IsSame: return "__is_same";
case BTT_TypeCompatible: return "__builtin_types_compatible_p";
case BTT_IsConvertibleTo: return "__is_convertible_to";
}
diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp
index d43a121..e75ce7d 100644
--- a/lib/AST/Type.cpp
+++ b/lib/AST/Type.cpp
@@ -1385,6 +1385,109 @@
return isa<RecordDecl>(TT->getDecl());
}
+static uint64_t countBasesWithFields(QualType BaseType) {
+ uint64_t BasesWithFields = 0;
+ if (const RecordType *T = BaseType->getAs<RecordType>()) {
+ CXXRecordDecl *RD = cast<CXXRecordDecl>(T->getDecl());
+ for (CXXRecordDecl::field_iterator Field = RD->field_begin(),
+ E = RD->field_end(); Field != E; ++Field)
+ BasesWithFields = 1;
+ for (CXXRecordDecl::base_class_const_iterator B = RD->bases_begin(),
+ BE = RD->bases_end(); B != BE; ++B)
+ BasesWithFields += countBasesWithFields(B->getType());
+ }
+ return BasesWithFields;
+}
+
+bool RecordType::hasStandardLayout(ASTContext& Context) const {
+ CXXRecordDecl *RD = cast<CXXRecordDecl>(getDecl());
+ if (! RD) {
+ assert(cast<RecordDecl>(getDecl()) &&
+ "RecordType does not have a corresponding RecordDecl");
+ return true;
+ }
+
+ // A standard-layout class is a class that:
+
+ for (CXXRecordDecl::method_iterator M = RD->method_begin(),
+ ME = RD->method_end(); M != ME; ++M) {
+ CXXMethodDecl *Method = *M;
+
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- has no virtual functions (10.3) [...]
+ if (Method->isVirtual())
+ return false;
+ }
+
+ AccessSpecifier AS = AS_none;
+ QualType FirstFieldType;
+ bool FirstFieldType_set = false;
+ uint64_t FieldCount = 0;
+
+ for (CXXRecordDecl::field_iterator Field = RD->field_begin(),
+ E = RD->field_end(); Field != E; ++Field, ++FieldCount) {
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- has no non-static data members of type non-standard-layout class
+ // (or array of such types) or reference [...]
+ QualType FieldType = Context.getBaseElementType((*Field)->getType());
+ if (const RecordType *T =
+ Context.getBaseElementType(FieldType)->getAs<RecordType>()) {
+ if (! T->hasStandardLayout(Context) || T->isReferenceType())
+ return false;
+ }
+ if (! FirstFieldType_set) {
+ FirstFieldType = FieldType;
+ FirstFieldType_set = true;
+ }
+
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- has the same access control (Clause 11) for all non-static data
+ // members [...]
+ if (AS == AS_none)
+ AS = (*Field)->getAccess();
+ else if (AS != (*Field)->getAccess())
+ return false;
+ }
+
+ for (CXXRecordDecl::base_class_const_iterator B = RD->bases_begin(),
+ BE = RD->bases_end(); B != BE; ++B) {
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- no virtual base classes (10.1) [...]
+ if (B->isVirtual())
+ return false;
+
+ QualType BT = B->getType();
+
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- has no non-standard-layout base classes [...]
+ if (const RecordType *T = BT->getAs<RecordType>())
+ if (! T->hasStandardLayout(Context))
+ return false;
+
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- has no base classes of the same type as the first non-static data
+ // member.
+ if (BT == FirstFieldType)
+ return false;
+
+ // C++0x [class]p7:
+ // A standard-layout class is a class that [...]
+ // -- either has no non-static data members in the most derived class
+ // and at most one base class with non-static data members, or has
+ // no base classes with non-static data members [...]
+ if (countBasesWithFields(BT) > (FieldCount == 0 ? 1 : 0))
+ return false;
+ }
+
+ return true;
+}
+
bool EnumType::classof(const TagType *TT) {
return isa<EnumDecl>(TT->getDecl());
}
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index f593028..c41798e 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -731,22 +731,19 @@
// styles of attributes?
MaybeParseCXX0XAttributes(attrs);
- if (TagType == DeclSpec::TST_struct && Tok.is(tok::kw___is_pod)) {
- // GNU libstdc++ 4.2 uses __is_pod as the name of a struct template, but
- // __is_pod is a keyword in GCC >= 4.3. Therefore, when we see the
- // token sequence "struct __is_pod", make __is_pod into a normal
- // identifier rather than a keyword, to allow libstdc++ 4.2 to work
- // properly.
- Tok.getIdentifierInfo()->RevertTokenIDToIdentifier();
- Tok.setKind(tok::identifier);
- }
-
- if (TagType == DeclSpec::TST_struct && Tok.is(tok::kw___is_empty)) {
- // GNU libstdc++ 4.2 uses __is_empty as the name of a struct template, but
- // __is_empty is a keyword in GCC >= 4.3. Therefore, when we see the
- // token sequence "struct __is_empty", make __is_empty into a normal
- // identifier rather than a keyword, to allow libstdc++ 4.2 to work
- // properly.
+ if (TagType == DeclSpec::TST_struct &&
+ (Tok.is(tok::kw___is_pod) ||
+ Tok.is(tok::kw___is_empty) ||
+ Tok.is(tok::kw___is_void) ||
+ Tok.is(tok::kw___is_pointer) ||
+ Tok.is(tok::kw___is_arithmetic) ||
+ Tok.is(tok::kw___is_fundamental) ||
+ Tok.is(tok::kw___is_scalar))) {
+ // GNU libstdc++ 4.2 uses certain intrinsic names as the name of
+ // struct templates, but these are keywords in GCC >= 4.3 and
+ // Clang. Therefore, when we see the token sequence "struct X", make
+ // X into a normal identifier rather than a keyword, to allow
+ // libstdc++ 4.2 to work properly.
Tok.getIdentifierInfo()->RevertTokenIDToIdentifier();
Tok.setKind(tok::identifier);
}
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index c990c52..1c4a942 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -519,6 +519,34 @@
/// '::'[opt] 'delete' cast-expression
/// '::'[opt] 'delete' '[' ']' cast-expression
///
+/// [GNU/Embarcadero] unary-type-trait:
+/// '__is_arithmetic'
+/// '__is_floating_point'
+/// '__is_integral'
+/// '__is_lvalue_expr'
+/// '__is_rvalue_expr'
+/// '__is_complete_type'
+/// '__is_void'
+/// '__is_array'
+/// '__is_function'
+/// '__is_reference'
+/// '__is_lvalue_reference'
+/// '__is_rvalue_reference'
+/// '__is_fundamental'
+/// '__is_object'
+/// '__is_scalar'
+/// '__is_compound'
+/// '__is_pointer'
+/// '__is_member_object_pointer'
+/// '__is_member_function_pointer'
+/// '__is_member_pointer'
+/// '__is_const'
+/// '__is_volatile'
+/// '__is_trivial'
+/// '__is_standard_layout'
+/// '__is_signed'
+/// '__is_unsigned'
+///
/// [GNU] unary-type-trait:
/// '__has_nothrow_assign'
/// '__has_nothrow_copy'
@@ -540,6 +568,8 @@
/// binary-type-trait:
/// [GNU] '__is_base_of'
/// [MS] '__is_convertible_to'
+/// '__is_convertible'
+/// '__is_same'
///
/// [Embarcadero] expression-trait:
/// '__is_lvalue_expr'
@@ -997,6 +1027,29 @@
case tok::kw___is_empty:
case tok::kw___is_enum:
case tok::kw___is_literal:
+ case tok::kw___is_arithmetic:
+ case tok::kw___is_integral:
+ case tok::kw___is_floating_point:
+ case tok::kw___is_complete_type:
+ case tok::kw___is_void:
+ case tok::kw___is_array:
+ case tok::kw___is_function:
+ case tok::kw___is_reference:
+ case tok::kw___is_lvalue_reference:
+ case tok::kw___is_rvalue_reference:
+ case tok::kw___is_fundamental:
+ case tok::kw___is_object:
+ case tok::kw___is_scalar:
+ case tok::kw___is_compound:
+ case tok::kw___is_pointer:
+ case tok::kw___is_member_object_pointer:
+ case tok::kw___is_member_function_pointer:
+ case tok::kw___is_member_pointer:
+ case tok::kw___is_const:
+ case tok::kw___is_volatile:
+ case tok::kw___is_standard_layout:
+ case tok::kw___is_signed:
+ case tok::kw___is_unsigned:
case tok::kw___is_literal_type:
case tok::kw___is_pod:
case tok::kw___is_polymorphic:
@@ -1014,6 +1067,8 @@
case tok::kw___builtin_types_compatible_p:
case tok::kw___is_base_of:
+ case tok::kw___is_same:
+ case tok::kw___is_convertible:
case tok::kw___is_convertible_to:
return ParseBinaryTypeTrait();
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 7bf76a9..aade050 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -1912,25 +1912,50 @@
static UnaryTypeTrait UnaryTypeTraitFromTokKind(tok::TokenKind kind) {
switch(kind) {
- default: llvm_unreachable("Not a known unary type trait");
+ 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_nothrow_copy: return UTT_HasNothrowCopy;
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_copy: return UTT_HasTrivialCopy;
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_arithmetic: return UTT_IsArithmetic;
+ case tok::kw___is_array: return UTT_IsArray;
case tok::kw___is_class: return UTT_IsClass;
+ case tok::kw___is_complete_type: return UTT_IsCompleteType;
+ case tok::kw___is_compound: return UTT_IsCompound;
+ case tok::kw___is_const: return UTT_IsConst;
case tok::kw___is_empty: return UTT_IsEmpty;
case tok::kw___is_enum: return UTT_IsEnum;
+ case tok::kw___is_floating_point: return UTT_IsFloatingPoint;
+ case tok::kw___is_function: return UTT_IsFunction;
+ case tok::kw___is_fundamental: return UTT_IsFundamental;
+ case tok::kw___is_integral: return UTT_IsIntegral;
+ case tok::kw___is_lvalue_expr: return UTT_IsLvalueExpr;
+ case tok::kw___is_lvalue_reference: return UTT_IsLvalueReference;
+ case tok::kw___is_member_function_pointer: return UTT_IsMemberFunctionPointer;
+ case tok::kw___is_member_object_pointer: return UTT_IsMemberObjectPointer;
+ case tok::kw___is_member_pointer: return UTT_IsMemberPointer;
+ case tok::kw___is_object: return UTT_IsObject;
case tok::kw___is_literal: return UTT_IsLiteral;
case tok::kw___is_literal_type: return UTT_IsLiteral;
case tok::kw___is_pod: return UTT_IsPOD;
+ case tok::kw___is_pointer: return UTT_IsPointer;
case tok::kw___is_polymorphic: return UTT_IsPolymorphic;
- case tok::kw___is_trivial: return UTT_IsTrivial;
+ case tok::kw___is_reference: return UTT_IsReference;
+ case tok::kw___is_rvalue_expr: return UTT_IsRvalueExpr;
+ case tok::kw___is_rvalue_reference: return UTT_IsRvalueReference;
+ case tok::kw___is_scalar: return UTT_IsScalar;
+ case tok::kw___is_signed: return UTT_IsSigned;
+ case tok::kw___is_standard_layout: return UTT_IsStandardLayout;
+ case tok::kw___is_trivial: return UTT_IsTrivial;
case tok::kw___is_union: return UTT_IsUnion;
+ case tok::kw___is_unsigned: return UTT_IsUnsigned;
+ case tok::kw___is_void: return UTT_IsVoid;
+ case tok::kw___is_volatile: return UTT_IsVolatile;
}
}
@@ -1938,6 +1963,8 @@
switch(kind) {
default: llvm_unreachable("Not a known binary type trait");
case tok::kw___is_base_of: return BTT_IsBaseOf;
+ case tok::kw___is_convertible: return BTT_IsConvertible;
+ case tok::kw___is_same: return BTT_IsSame;
case tok::kw___builtin_types_compatible_p: return BTT_TypeCompatible;
case tok::kw___is_convertible_to: return BTT_IsConvertibleTo;
}
diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
index f8ad763..7086176 100644
--- a/lib/Sema/SemaExprCXX.cpp
+++ b/lib/Sema/SemaExprCXX.cpp
@@ -2379,6 +2379,7 @@
return false;
case UTT_IsAbstract:
if (const RecordType *RT = T->getAs<RecordType>())
+ if (!Self.RequireCompleteType(KeyLoc, T, diag::err_incomplete_typeid))
return cast<CXXRecordDecl>(RT->getDecl())->isAbstract();
return false;
case UTT_IsEmpty:
@@ -2387,6 +2388,74 @@
&& cast<CXXRecordDecl>(Record->getDecl())->isEmpty();
}
return false;
+ case UTT_IsIntegral:
+ return T->isIntegralType(C);
+ case UTT_IsFloatingPoint:
+ return T->isFloatingType();
+ case UTT_IsArithmetic:
+ return T->isArithmeticType() && ! T->isEnumeralType();
+ case UTT_IsArray:
+ return T->isArrayType();
+ case UTT_IsCompleteType:
+ return ! T->isIncompleteType();
+ case UTT_IsCompound:
+ return ! (T->isVoidType() || T->isArithmeticType()) || T->isEnumeralType();
+ case UTT_IsConst:
+ return T.isConstQualified();
+ case UTT_IsFunction:
+ return T->isFunctionType();
+ case UTT_IsFundamental:
+ return T->isVoidType() || (T->isArithmeticType() && ! T->isEnumeralType());
+ case UTT_IsLvalueReference:
+ return T->isLValueReferenceType();
+ case UTT_IsMemberFunctionPointer:
+ return T->isMemberFunctionPointerType();
+ case UTT_IsMemberObjectPointer:
+ return T->isMemberDataPointerType();
+ case UTT_IsMemberPointer:
+ return T->isMemberPointerType();
+ case UTT_IsObject:
+ // Defined in Section 3.9 p8 of the Working Draft, essentially:
+ // !__is_reference(T) && !__is_function(T) && !__is_void(T).
+ return ! (T->isReferenceType() || T->isFunctionType() || T->isVoidType());
+ case UTT_IsPointer:
+ return T->isPointerType();
+ case UTT_IsReference:
+ return T->isReferenceType();
+ case UTT_IsRvalueReference:
+ return T->isRValueReferenceType();
+ case UTT_IsScalar:
+ // Scalar type is defined in Section 3.9 p10 of the Working Draft.
+ // Essentially:
+ // __is_arithmetic( T ) || __is_enumeration(T) ||
+ // __is_pointer(T) || __is_member_pointer(T)
+ return (T->isArithmeticType() || T->isEnumeralType() ||
+ T->isPointerType() || T->isMemberPointerType());
+ case UTT_IsSigned:
+ return T->isSignedIntegerType();
+ case UTT_IsStandardLayout:
+ // Error if T is an incomplete type.
+ if (Self.RequireCompleteType(KeyLoc, T, diag::err_incomplete_typeid))
+ return false;
+
+ // A standard layout type is:
+ // - a scalar type
+ // - an array of standard layout types
+ // - a standard layout class type:
+ if (EvaluateUnaryTypeTrait(Self, UTT_IsScalar, T, KeyLoc))
+ return true;
+ if (EvaluateUnaryTypeTrait(Self, UTT_IsScalar, C.getBaseElementType(T),
+ KeyLoc))
+ return true;
+ if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>())
+ return RT->hasStandardLayout(C);
+ return false;
+ case UTT_IsUnsigned:
+ return T->isUnsignedIntegerType();
+ case UTT_IsVoid:
+ return T->isVoidType();
+ case UTT_IsVolatile:
+ return T.isVolatileQualified();
case UTT_HasTrivialConstructor:
// http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html:
// If __is_pod (type) is true then the trait is true, else if type is
@@ -2579,11 +2648,12 @@
// According to http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html
// all traits except __is_class, __is_enum and __is_union require a the type
// to be complete, an array of unknown bound, or void.
- if (UTT != UTT_IsClass && UTT != UTT_IsEnum && UTT != UTT_IsUnion) {
+ if (UTT != UTT_IsClass && UTT != UTT_IsEnum && UTT != UTT_IsUnion &&
+ UTT != UTT_IsCompleteType) {
QualType E = T;
if (T->isIncompleteArrayType())
E = Context.getAsArrayType(T)->getElementType();
- if (!T->isVoidType() &&
+ if (!T->isVoidType() && ! LangOpts.Borland &&
RequireCompleteType(KWLoc, E,
diag::err_incomplete_type_used_in_type_trait_expr))
return ExprError();
@@ -2651,11 +2721,12 @@
return cast<CXXRecordDecl>(rhsRecord->getDecl())
->isDerivedFrom(cast<CXXRecordDecl>(lhsRecord->getDecl()));
}
-
+ case BTT_IsSame:
+ return Self.Context.hasSameType(LhsT, RhsT);
case BTT_TypeCompatible:
return Self.Context.typesAreCompatible(LhsT.getUnqualifiedType(),
RhsT.getUnqualifiedType());
-
+ case BTT_IsConvertible:
case BTT_IsConvertibleTo: {
// C++0x [meta.rel]p4:
// Given the following function prototype:
@@ -2730,6 +2801,8 @@
QualType ResultType;
switch (BTT) {
case BTT_IsBaseOf: ResultType = Context.BoolTy; break;
+ case BTT_IsConvertible: ResultType = Context.BoolTy; break;
+ case BTT_IsSame: ResultType = Context.BoolTy; break;
case BTT_TypeCompatible: ResultType = Context.IntTy; break;
case BTT_IsConvertibleTo: ResultType = Context.BoolTy; break;
}