forked from OSchip/llvm-project
Completely re-implement the core logic behind the __is_standard_layout
type trait. The previous implementation suffered from several problems: 1) It implemented all of the logic in RecordType by walking over every base and field in a CXXRecordDecl and validating the constraints of the standard. This made for very straightforward code, but is extremely inefficient. It also is conceptually wrong, the logic tied to the C++ definition of standard-layout classes should be in CXXRecordDecl, not RecordType. 2) To address the performance problems with #1, a cache bit was added to CXXRecordDecl, and at the completion of every C++ class, the RecordType was queried to determine if it was a standard layout class, and that state was cached. Two things went very very wrong with this. First, the caching version of the query *was never called*. Even within the recursive steps of the walk over all fields and bases the caching variant was not called, making each query a full *recursive* walk. Second, despite the cache not being used, it was computed for every class declared, even when the trait was never used in the program. This probably significantly regressed compile time performance for edge-case files. 3) An ASTContext was required merely to query the type trait because querying it performed the actual computations. 4) The caching bit wasn't managed correctly (uninitialized). The new implementation follows the system for all the other traits on C++ classes by encoding all the state needed in the definition data and building up the trait incrementally as each base and member are added to the definition of the class. The idiosyncracies of the specification of standard-layout classes requires more state than I would like; currently 5 bits. I could eliminate one of the bits easily at the expense of both clarity and resilience of the code. I might be able to eliminate one of the other bits by computing its state in terms of other state bits in the definition. I've already done that in one place where there was a fairly simple way to achieve it. It's possible some of the bits could be moved out of the definition data and into some other structure which isn't serialized if the serialized bloat is a problem. That would preclude serialization of a partial class declaration, but that's likely already precluded. Comments on any of these issues welcome. llvm-svn: 130601
This commit is contained in:
parent
08635fcc83
commit
b196374f53
|
@ -321,6 +321,22 @@ class CXXRecordDecl : public RecordDecl {
|
|||
/// member.
|
||||
bool HasStandardLayout : 1;
|
||||
|
||||
/// HasNoNonEmptyBases - True when there are no non-empty base classes.
|
||||
///
|
||||
/// This is a helper bit of state used to implement HasStandardLayout more
|
||||
/// efficiently.
|
||||
bool HasNoNonEmptyBases : 1;
|
||||
|
||||
/// HasPrivateFields - True when there are private non-static data members.
|
||||
bool HasPrivateFields : 1;
|
||||
|
||||
/// HasProtectedFields - True when there are protected non-static data
|
||||
/// members.
|
||||
bool HasProtectedFields : 1;
|
||||
|
||||
/// HasPublicFields - True when there are private non-static data members.
|
||||
bool HasPublicFields : 1;
|
||||
|
||||
/// HasTrivialConstructor - True when this class has a trivial constructor.
|
||||
///
|
||||
/// C++ [class.ctor]p5. A constructor is trivial if it is an
|
||||
|
@ -780,8 +796,8 @@ public:
|
|||
/// which means that the class contains or inherits a pure virtual function.
|
||||
bool isAbstract() const { return data().Abstract; }
|
||||
|
||||
// hasStandardLayout - Whether this class has standard layout
|
||||
// (C++ [class]p7)
|
||||
/// hasStandardLayout - Whether this class has standard layout
|
||||
/// (C++ [class]p7)
|
||||
bool hasStandardLayout() const { return data().HasStandardLayout; }
|
||||
|
||||
// hasTrivialConstructor - Whether this class has a trivial constructor
|
||||
|
|
|
@ -2843,7 +2843,7 @@ public:
|
|||
bool hasConstFields() const { return false; }
|
||||
|
||||
/// \brief Whether this class has standard layout
|
||||
bool hasStandardLayout(ASTContext& Ctx) const;
|
||||
bool hasStandardLayout() const;
|
||||
|
||||
bool isSugared() const { return false; }
|
||||
QualType desugar() const { return QualType(this, 0); }
|
||||
|
|
|
@ -31,8 +31,9 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
|
|||
: UserDeclaredConstructor(false), UserDeclaredCopyConstructor(false),
|
||||
UserDeclaredCopyAssignment(false), UserDeclaredDestructor(false),
|
||||
Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
|
||||
Abstract(false), HasStandardLayout(true), HasTrivialConstructor(true),
|
||||
HasConstExprNonCopyMoveConstructor(false),
|
||||
Abstract(false), HasStandardLayout(true), HasNoNonEmptyBases(true),
|
||||
HasPrivateFields(false), HasProtectedFields(false), HasPublicFields(false),
|
||||
HasTrivialConstructor(true), HasConstExprNonCopyMoveConstructor(false),
|
||||
HasTrivialCopyConstructor(true), HasTrivialMoveConstructor(true),
|
||||
HasTrivialCopyAssignment(true), HasTrivialMoveAssignment(true),
|
||||
HasTrivialDestructor(true), HasNonLiteralTypeFieldsOrBases(false),
|
||||
|
@ -111,8 +112,22 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
|||
|
||||
// A class with a non-empty base class is not empty.
|
||||
// FIXME: Standard ref?
|
||||
if (!BaseClassDecl->isEmpty())
|
||||
if (!BaseClassDecl->isEmpty()) {
|
||||
if (!data().Empty) {
|
||||
// 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, and
|
||||
// If this is the second non-empty base, then neither of these two
|
||||
// clauses can be true.
|
||||
data().HasStandardLayout = false;
|
||||
}
|
||||
|
||||
data().Empty = false;
|
||||
data().HasNoNonEmptyBases = false;
|
||||
}
|
||||
|
||||
// C++ [class.virtual]p1:
|
||||
// A class that declares or inherits a virtual function is called a
|
||||
|
@ -120,6 +135,12 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
|||
if (BaseClassDecl->isPolymorphic())
|
||||
data().Polymorphic = true;
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that: [...]
|
||||
// -- has no non-standard-layout base classes
|
||||
if (!BaseClassDecl->hasStandardLayout())
|
||||
data().HasStandardLayout = false;
|
||||
|
||||
// Record if this base is the first non-literal field or base.
|
||||
if (!hasNonLiteralTypeFieldsOrBases() && !BaseType->isLiteralType())
|
||||
data().HasNonLiteralTypeFieldsOrBases = true;
|
||||
|
@ -160,6 +181,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases,
|
|||
// -- class X has no virtual functions and no virtual base classes, and
|
||||
data().HasTrivialCopyAssignment = false;
|
||||
data().HasTrivialMoveAssignment = false;
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that: [...]
|
||||
// -- has [...] no virtual base classes
|
||||
data().HasStandardLayout = false;
|
||||
} else {
|
||||
// C++ [class.ctor]p5:
|
||||
// A constructor is trivial if all the direct base classes of its
|
||||
|
@ -410,6 +436,11 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
data().HasTrivialCopyAssignment = false;
|
||||
data().HasTrivialMoveAssignment = false;
|
||||
// FIXME: Destructor?
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that: [...]
|
||||
// -- has no virtual functions
|
||||
data().HasStandardLayout = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,7 +652,21 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
data().Aggregate = false;
|
||||
data().PlainOldData = false;
|
||||
}
|
||||
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that:
|
||||
// [...]
|
||||
// -- has the same access control for all non-static data members,
|
||||
switch (D->getAccess()) {
|
||||
case AS_private: data().HasPrivateFields = true; break;
|
||||
case AS_protected: data().HasProtectedFields = true; break;
|
||||
case AS_public: data().HasPublicFields = true; break;
|
||||
case AS_none: assert(0 && "Invalid access specifier");
|
||||
};
|
||||
if ((data().HasPrivateFields + data().HasProtectedFields +
|
||||
data().HasPublicFields) > 1)
|
||||
data().HasStandardLayout = false;
|
||||
|
||||
// C++0x [class]p9:
|
||||
// A POD struct is a class that is both a trivial class and a
|
||||
// standard-layout class, and has no non-static data members of type
|
||||
|
@ -630,9 +675,15 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
QualType T = Context.getBaseElementType(Field->getType());
|
||||
if (!T->isPODType())
|
||||
data().PlainOldData = false;
|
||||
if (T->isReferenceType())
|
||||
if (T->isReferenceType()) {
|
||||
data().HasTrivialConstructor = false;
|
||||
|
||||
// C++0x [class]p7:
|
||||
// A standard-layout class is a class that:
|
||||
// -- has no non-static data members of type [...] reference,
|
||||
data().HasStandardLayout = false;
|
||||
}
|
||||
|
||||
// Record if this field is the first non-literal field or base.
|
||||
if (!hasNonLiteralTypeFieldsOrBases() && !T->isLiteralType())
|
||||
data().HasNonLiteralTypeFieldsOrBases = true;
|
||||
|
@ -669,9 +720,53 @@ void CXXRecordDecl::addedMember(Decl *D) {
|
|||
|
||||
if (!FieldRec->hasTrivialDestructor())
|
||||
data().HasTrivialDestructor = false;
|
||||
|
||||
// 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) [...]
|
||||
if (!FieldRec->hasStandardLayout())
|
||||
data().HasStandardLayout = 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.
|
||||
// We don't want to expend bits in the state of the record decl
|
||||
// tracking whether this is the first non-static data member so we
|
||||
// cheat a bit and use some of the existing state: the empty bit.
|
||||
// Virtual bases and virtual methods make a class non-empty, but they
|
||||
// also make it non-standard-layout so we needn't check here.
|
||||
// A non-empty base class may leave the class standard-layout, but not
|
||||
// if we have arrived here, and have at least on non-static data
|
||||
// member. If HasStandardLayout remains true, then the first non-static
|
||||
// data member must come through here with Empty still true, and Empty
|
||||
// will subsequently be set to false below.
|
||||
if (data().HasStandardLayout && data().Empty) {
|
||||
for (CXXRecordDecl::base_class_const_iterator BI = bases_begin(),
|
||||
BE = bases_end();
|
||||
BI != BE; ++BI) {
|
||||
if (Context.hasSameUnqualifiedType(BI->getType(), T)) {
|
||||
data().HasStandardLayout = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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, and
|
||||
// At this point we know that we have a non-static data member, so the last
|
||||
// clause holds.
|
||||
if (!data().HasNoNonEmptyBases)
|
||||
data().HasStandardLayout = false;
|
||||
|
||||
// If this is not a zero-length bit-field, then the class is not empty.
|
||||
if (data().Empty) {
|
||||
if (!Field->getBitWidth())
|
||||
|
@ -926,10 +1021,6 @@ CXXDestructorDecl *CXXRecordDecl::getDestructor() const {
|
|||
|
||||
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) {
|
||||
|
|
|
@ -1448,7 +1448,7 @@ static uint64_t countBasesWithFields(QualType BaseType) {
|
|||
return BasesWithFields;
|
||||
}
|
||||
|
||||
bool RecordType::hasStandardLayout(ASTContext& Context) const {
|
||||
bool RecordType::hasStandardLayout() const {
|
||||
CXXRecordDecl *RD = cast<CXXRecordDecl>(getDecl());
|
||||
if (! RD) {
|
||||
assert(cast<RecordDecl>(getDecl()) &&
|
||||
|
@ -1456,85 +1456,7 @@ bool RecordType::hasStandardLayout(ASTContext& Context) const {
|
|||
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;
|
||||
return RD->hasStandardLayout();
|
||||
}
|
||||
|
||||
bool EnumType::classof(const TagType *TT) {
|
||||
|
|
|
@ -2452,7 +2452,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, QualType T,
|
|||
KeyLoc))
|
||||
return true;
|
||||
if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>())
|
||||
return RT->hasStandardLayout(C);
|
||||
return RT->hasStandardLayout();
|
||||
return false;
|
||||
case UTT_IsUnsigned:
|
||||
return T->isUnsignedIntegerType();
|
||||
|
|
|
@ -843,6 +843,10 @@ void ASTDeclReader::ReadCXXDefinitionData(
|
|||
Data.Polymorphic = Record[Idx++];
|
||||
Data.Abstract = Record[Idx++];
|
||||
Data.HasStandardLayout = Record[Idx++];
|
||||
Data.HasNoNonEmptyBases = Record[Idx++];
|
||||
Data.HasPrivateFields = Record[Idx++];
|
||||
Data.HasProtectedFields = Record[Idx++];
|
||||
Data.HasPublicFields = Record[Idx++];
|
||||
Data.HasTrivialConstructor = Record[Idx++];
|
||||
Data.HasConstExprNonCopyMoveConstructor = Record[Idx++];
|
||||
Data.HasTrivialCopyConstructor = Record[Idx++];
|
||||
|
|
|
@ -3783,6 +3783,10 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
|
|||
Record.push_back(Data.Polymorphic);
|
||||
Record.push_back(Data.Abstract);
|
||||
Record.push_back(Data.HasStandardLayout);
|
||||
Record.push_back(Data.HasNoNonEmptyBases);
|
||||
Record.push_back(Data.HasPrivateFields);
|
||||
Record.push_back(Data.HasProtectedFields);
|
||||
Record.push_back(Data.HasPublicFields);
|
||||
Record.push_back(Data.HasTrivialConstructor);
|
||||
Record.push_back(Data.HasConstExprNonCopyMoveConstructor);
|
||||
Record.push_back(Data.HasTrivialCopyConstructor);
|
||||
|
|
Loading…
Reference in New Issue