[cfe-commits] PATCH [2/2]: Implementation of Embarcadero type traits

Douglas Gregor dgregor at apple.com
Mon Feb 21 17:58:35 PST 2011


On Feb 18, 2011, at 2:55 PM, John Wiegley wrote:

> 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).
>> From 986e7eac4d977027198428c3ee5d33ce8d6db361 Mon Sep 17 00:00:00 2001
> From: John Wiegley <johnw at newartisans.com>
> Date: Thu, 17 Feb 2011 00:44:24 -0500
> Subject: [PATCH 2/2] Implementation of Embarcadero unary and binary type traits
> 
> Embarcadero Unary Type Traits
> 
> __is_arithmetic
> __is_floating_point
> __is_integral
> __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
> 
> Embarcadero Binary Type Traits
> 
> __is_same
> __is_convertible

That's a lot of traits!

> +static uint64_t countBasesWithFields(QualType BaseType) {
> +  const RecordType *T = BaseType->getAs<RecordType>();
> +  CXXRecordDecl *RD = cast<CXXRecordDecl>(T->getDecl());
> +  uint64_t BasesWithFields = 0;
> +  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;
> +
> +    // - has no virtual functions (10.3) and
> +    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) {
> +    // - 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 = FieldType->getAs<RecordType>()) {
> +      if (! T->hasStandardLayout(Context))
> +        return false;
> +    }
> +    if (! FirstFieldType_set) {
> +      FirstFieldType = FieldType;
> +      FirstFieldType_set = true;
> +    }
> +  
> +    // - 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) {
> +    // - no virtual base classes (10.1),
> +    if (B->isVirtual())
> +      return false;
> +
> +    QualType BT = B->getType();
> +    const RecordType *T = BT->getAs<RecordType>();
> +    // - has no non-standard-layout base classes,
> +    if (! T->hasStandardLayout(Context))
> +      return false;
> +
> +    // - has no base classes of the same type as the first non-static
> +    //   data member.
> +    if (BT == FirstFieldType)
> +      return false;
> +
> +    // - 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, and
> +    if (countBasesWithFields(BT) > (FieldCount == 0 ? 1 : 0))
> +      return false;
> +  }
> +
> +  return true;
> +}

I'd much rather that you add a 'StandardLayout' bit to CXXRecordDecl, and keep it updated when declarations are added to the CXXRecordDecl, like we do with the POD, Aggregate, Polymorphic, etc., bits. We'll likely need to update semantic analysis with checks for standard-layout classes anyway, and we don't want those checks to have to walk the entire class each time.

> diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp
> index e3eb129..81dae6c 100644
> --- a/lib/Sema/SemaExprCXX.cpp
> +++ b/lib/Sema/SemaExprCXX.cpp
> @@ -2284,6 +2284,107 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T,
>           && 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();

Okay, __is_complete_type is a very scary trait. Should we try to complete the type before answering "false", in the case where this is a class template specialization or member thereof ? Or do we look to see if the type can be completed, without actually trying to complete it?

> +  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_IsLvalueExpr:
> +    return false; // jww (2010-12-29): NYI.  These aren't type traits at
> +                  // all, they're really expression traits, so they
> +                  // belong in ExprCXX.cpp.
> +  case UTT_IsRvalueExpr:
> +    return ! EvaluateUnaryTypeTrait(Self, UTT_IsLvalueExpr, T, KeyLoc);

UTT_IsLvalueExpr/UTT_IsRvalueExpr were part of the other patch, IIRC.

> +  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);
> +    if (const RecordType *RT = T->getAs<RecordType>())
> +      return RT->hasStandardLayout©;

This second "if" is redundant,  The base element type of a record type is the record type itself.

> +    return false;
> +  case UTT_IsTrivial:
> +    // Trivial types are defined in Section 3.9 p10 of the Working Draft.
> +    // Trivial classes are defined in Section 9 p5 of the Working Draft.
> +    // Returns true if T is both const and volatile qualified.
> +    // A type is trivial if it is:
> +    //  - a scalar type
> +    //  - an array of trivial types
> +    //  - a class/union type where all 4 special members are trivial:
> +    //    + default constructor
> +    //    + copy constructor
> +    //    + copy assignment operator
> +    //    + destructor
> +    if (T.isConstQualified() && T.isVolatileQualified())
> +      return true;
> +    if (EvaluateUnaryTypeTrait(Self, UTT_IsScalar, T, KeyLoc) ||
> +        (T->isArrayType() &&
> +         EvaluateUnaryTypeTrait(Self, UTT_IsScalar, C.getBaseElementType(T), KeyLoc)))
> +      return true;
> +    if (C.getBaseElementType(T)->getAs<RecordType>())
> +      if (EvaluateUnaryTypeTrait(Self, UTT_HasTrivialConstructor, T, KeyLoc) &&
> +          EvaluateUnaryTypeTrait(Self, UTT_HasTrivialCopy, T, KeyLoc) &&
> +          EvaluateUnaryTypeTrait(Self, UTT_HasTrivialAssign, T, KeyLoc) &&
> +          EvaluateUnaryTypeTrait(Self, UTT_HasTrivialDestructor, T, KeyLoc))
> +        return true;
> +    return false;

This is another case where I would like CXXRecordDecl to be extended with a 'Trivial' bit that's maintained as declarations are added to the CXXRecordDecl.

It would be helpful if a follow-on patch could add documentation for these traits to

	http://clang.llvm.org/docs/LanguageExtensions.html#checking_type_traits

and also add appropriate __has_feature support for each of the traits, so that libraries can detect which traits are available.

	- Doug





More information about the cfe-commits mailing list