From 1827403a7138946305c0058f262e02b595cf882f Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Sat, 3 Jul 2010 00:47:00 +0000 Subject: [PATCH] Lazily declare default constructors. We now delay the construction of declarations for implicit default constructors, copy constructors, copy assignment operators, and destructors. On a "simple" translation unit that includes a bunch of C++ standard library headers, we generate relatively few of these implicit declarations now: 4/159 implicit default constructors created 18/236 implicit copy constructors created 70/241 implicit copy assignment operators created 0/173 implicit destructors created And, on this translation unit, this optimization doesn't really provide any benefit. I'll do some more performance measurements soon, but this completes the implementation work for . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@107551 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/ASTContext.h | 9 +++- include/clang/AST/DeclCXX.h | 20 ++++++++- lib/AST/ASTContext.cpp | 5 +++ lib/AST/DeclBase.cpp | 6 +++ lib/AST/DeclCXX.cpp | 5 ++- lib/Frontend/PCHReaderDecl.cpp | 1 + lib/Frontend/PCHWriterDecl.cpp | 1 + lib/Sema/SemaDeclCXX.cpp | 80 ++++++++++++++++++++++++++++------ lib/Sema/SemaLookup.cpp | 24 +++++++--- 9 files changed, 128 insertions(+), 23 deletions(-) diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 19b5694f0a..2b65f3155e 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -1327,10 +1327,17 @@ public: // Statistics //===--------------------------------------------------------------------===// + /// \brief The number of implicitly-declared default constructors. + static unsigned NumImplicitDefaultConstructors; + + /// \brief The number of implicitly-declared default constructors for + /// which declarations were built. + static unsigned NumImplicitDefaultConstructorsDeclared; + /// \brief The number of implicitly-declared copy constructors. static unsigned NumImplicitCopyConstructors; - /// \brief The number of implicitly-declared constructors for + /// \brief The number of implicitly-declared copy constructors for /// which declarations were built. static unsigned NumImplicitCopyConstructorsDeclared; diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 90302afc4c..98ab83c6a6 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -318,7 +318,11 @@ class CXXRecordDecl : public RecordDecl { /// ComputedVisibleConversions - True when visible conversion functions are /// already computed and are available. bool ComputedVisibleConversions : 1; - + + /// \brief Whether we have already declared the default constructor or + /// do not need to have one declared. + bool DeclaredDefaultConstructor : 1; + /// \brief Whether we have already declared the copy constructor. bool DeclaredCopyConstructor : 1; @@ -540,6 +544,20 @@ public: return data().FirstFriend != 0; } + /// \brief Determine whether this class has had its default constructor + /// declared implicitly or does not need one declared implicitly. + /// + /// This value is used for lazy creation of default constructors. + bool hasDeclaredDefaultConstructor() const { + return data().DeclaredDefaultConstructor; + } + + /// \brief Note whether this class has already had its default constructor + /// implicitly declared or doesn't need one. + void setDeclaredDefaultConstructor(bool DDC) { + data().DeclaredDefaultConstructor = DDC; + } + /// hasConstCopyConstructor - Determines whether this class has a /// copy constructor that accepts a const-qualified argument. bool hasConstCopyConstructor(ASTContext &Context) const; diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 4737abc3e3..2d52bd08d5 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -31,6 +31,8 @@ using namespace clang; +unsigned ASTContext::NumImplicitDefaultConstructors; +unsigned ASTContext::NumImplicitDefaultConstructorsDeclared; unsigned ASTContext::NumImplicitCopyConstructors; unsigned ASTContext::NumImplicitCopyConstructorsDeclared; unsigned ASTContext::NumImplicitCopyAssignmentOperators; @@ -261,6 +263,9 @@ void ASTContext::PrintStats() const { fprintf(stderr, "Total bytes = %d\n", int(TotalBytes)); // Implicit special member functions. + fprintf(stderr, " %u/%u implicit default constructors created\n", + NumImplicitDefaultConstructorsDeclared, + NumImplicitDefaultConstructors); fprintf(stderr, " %u/%u implicit copy constructors created\n", NumImplicitCopyConstructorsDeclared, NumImplicitCopyConstructors); diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 1c41e5a659..8020916a6c 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -975,6 +975,12 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D) { CreateStoredDeclsMap(*C); } + // If there is an external AST source, load any declarations it knows about + // with this declaration's name. + if (ExternalASTSource *Source = getParentASTContext().getExternalSource()) + if (hasExternalVisibleStorage()) + Source->FindExternalVisibleDeclsByName(this, D->getDeclName()); + // Insert this declaration into the map. StoredDeclsList &DeclNameEntries = (*LookupPtr)[D->getDeclName()]; if (DeclNameEntries.isNull()) { diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 4b1909e57c..7ce9c64de0 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -32,7 +32,7 @@ CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D) Abstract(false), HasTrivialConstructor(true), HasTrivialCopyConstructor(true), HasTrivialCopyAssignment(true), HasTrivialDestructor(true), ComputedVisibleConversions(false), - DeclaredCopyConstructor(false), + DeclaredDefaultConstructor(false), DeclaredCopyConstructor(false), DeclaredCopyAssignment(false), DeclaredDestructor(false), Bases(0), NumBases(0), VBases(0), NumVBases(0), Definition(D), FirstFriend(0) { @@ -277,6 +277,9 @@ CXXRecordDecl::addedConstructor(ASTContext &Context, // Note that we have a user-declared constructor. data().UserDeclaredConstructor = true; + // Note that we have no need of an implicitly-declared default constructor. + data().DeclaredDefaultConstructor = true; + // C++ [dcl.init.aggr]p1: // An aggregate is an array or a class (clause 9) with no // user-declared constructors (12.1) [...]. diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp index a7b092abb9..8e0d635508 100644 --- a/lib/Frontend/PCHReaderDecl.cpp +++ b/lib/Frontend/PCHReaderDecl.cpp @@ -657,6 +657,7 @@ void PCHDeclReader::VisitCXXRecordDecl(CXXRecordDecl *D) { Data.HasTrivialCopyAssignment = Record[Idx++]; Data.HasTrivialDestructor = Record[Idx++]; Data.ComputedVisibleConversions = Record[Idx++]; + Data.DeclaredDefaultConstructor = Record[Idx++]; Data.DeclaredCopyConstructor = Record[Idx++]; Data.DeclaredCopyAssignment = Record[Idx++]; Data.DeclaredDestructor = Record[Idx++]; diff --git a/lib/Frontend/PCHWriterDecl.cpp b/lib/Frontend/PCHWriterDecl.cpp index 3a053e283d..2383a75bd0 100644 --- a/lib/Frontend/PCHWriterDecl.cpp +++ b/lib/Frontend/PCHWriterDecl.cpp @@ -654,6 +654,7 @@ void PCHDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) { Record.push_back(Data.HasTrivialCopyAssignment); Record.push_back(Data.HasTrivialDestructor); Record.push_back(Data.ComputedVisibleConversions); + Record.push_back(Data.DeclaredDefaultConstructor); Record.push_back(Data.DeclaredCopyConstructor); Record.push_back(Data.DeclaredCopyAssignment); Record.push_back(Data.DeclaredDestructor); diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 5c98e2b1db..4dd5e3f30d 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -2654,7 +2654,7 @@ namespace { /// definition of the class is complete. void Sema::AddImplicitlyDeclaredMembersToClass(CXXRecordDecl *ClassDecl) { if (!ClassDecl->hasUserDeclaredConstructor()) - DeclareImplicitDefaultConstructor(ClassDecl); + ++ASTContext::NumImplicitDefaultConstructors; if (!ClassDecl->hasUserDeclaredCopyConstructor()) ++ASTContext::NumImplicitCopyConstructors; @@ -4156,6 +4156,19 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( // user-declared constructor for class X, a default constructor is // implicitly declared. An implicitly-declared default constructor // is an inline public member of its class. + assert(!ClassDecl->hasUserDeclaredConstructor() && + "Should not build implicit default constructor!"); + + // FIXME: HACK HACK HACK + if (Context.getExternalSource()) { + // This hack ensures that, when using precompiled headers, the lookup + // table in the DeclContext has already loaded the constructor declarations + // so that we can add a new one. The real fix will go into DeclContext, + // when I figure out what that is. + CanQualType T + = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); + ClassDecl->lookup(Context.DeclarationNames.getCXXConstructorName(T)); + } // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an @@ -4169,20 +4182,28 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( if (B->isVirtual()) // Handled below. continue; - if (const RecordType *BaseType = B->getType()->getAs()) - if (CXXConstructorDecl *Constructor - = cast(BaseType->getDecl())->getDefaultConstructor()) + if (const RecordType *BaseType = B->getType()->getAs()) { + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + if (!BaseClassDecl->hasDeclaredDefaultConstructor()) + ExceptSpec.CalledDecl(DeclareImplicitDefaultConstructor(BaseClassDecl)); + else if (CXXConstructorDecl *Constructor + = BaseClassDecl->getDefaultConstructor()) ExceptSpec.CalledDecl(Constructor); + } } // Virtual base-class destructors. for (CXXRecordDecl::base_class_iterator B = ClassDecl->vbases_begin(), BEnd = ClassDecl->vbases_end(); B != BEnd; ++B) { - if (const RecordType *BaseType = B->getType()->getAs()) - if (CXXConstructorDecl *Constructor - = cast(BaseType->getDecl())->getDefaultConstructor()) + if (const RecordType *BaseType = B->getType()->getAs()) { + CXXRecordDecl *BaseClassDecl = cast(BaseType->getDecl()); + if (!BaseClassDecl->hasDeclaredDefaultConstructor()) + ExceptSpec.CalledDecl(DeclareImplicitDefaultConstructor(BaseClassDecl)); + else if (CXXConstructorDecl *Constructor + = BaseClassDecl->getDefaultConstructor()) ExceptSpec.CalledDecl(Constructor); + } } // Field destructors. @@ -4190,10 +4211,15 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( FEnd = ClassDecl->field_end(); F != FEnd; ++F) { if (const RecordType *RecordTy - = Context.getBaseElementType(F->getType())->getAs()) - if (CXXConstructorDecl *Constructor - = cast(RecordTy->getDecl())->getDefaultConstructor()) + = Context.getBaseElementType(F->getType())->getAs()) { + CXXRecordDecl *FieldClassDecl = cast(RecordTy->getDecl()); + if (!FieldClassDecl->hasDeclaredDefaultConstructor()) + ExceptSpec.CalledDecl( + DeclareImplicitDefaultConstructor(FieldClassDecl)); + else if (CXXConstructorDecl *Constructor + = FieldClassDecl->getDefaultConstructor()) ExceptSpec.CalledDecl(Constructor); + } } @@ -4219,10 +4245,15 @@ CXXConstructorDecl *Sema::DeclareImplicitDefaultConstructor( DefaultCon->setAccess(AS_public); DefaultCon->setImplicit(); DefaultCon->setTrivial(ClassDecl->hasTrivialConstructor()); + + // Note that we have declared this constructor. + ClassDecl->setDeclaredDefaultConstructor(true); + ++ASTContext::NumImplicitDefaultConstructorsDeclared; + if (Scope *S = getScopeForContext(ClassDecl)) - PushOnScopeChains(DefaultCon, S, true); - else - ClassDecl->addDecl(DefaultCon); + PushOnScopeChains(DefaultCon, S, false); + ClassDecl->addDecl(DefaultCon); + return DefaultCon; } @@ -4612,7 +4643,17 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { // 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. - // + + + // FIXME: HACK HACK HACK + if (Context.getExternalSource()) { + // This hack ensures that, when using precompiled headers, the lookup + // table in the DeclContext has already loaded the assignment operator + // declarations so that we can add a new one. The real fix will go into + // DeclContext, when I figure out what that is. + ClassDecl->lookup(Context.DeclarationNames.getCXXOperatorName(OO_Equal)); + } + // C++ [class.copy]p10: // If the class definition does not explicitly declare a copy // assignment operator, one is declared implicitly. @@ -5045,6 +5086,17 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( // If the class definition does not explicitly declare a copy // constructor, one is declared implicitly. + // FIXME: HACK HACK HACK + if (Context.getExternalSource()) { + // This hack ensures that, when using precompiled headers, the lookup + // table in the DeclContext has already loaded the constructor declarations + // so that we can add a new one. The real fix will go into DeclContext, + // when I figure out what that is. + CanQualType T + = Context.getCanonicalType(Context.getTypeDeclType(ClassDecl)); + ClassDecl->lookup(Context.DeclarationNames.getCXXConstructorName(T)); + } + // C++ [class.copy]p5: // The implicitly-declared copy constructor for a class X will // have the form diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index b13deec19a..c11e3b371d 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -466,6 +466,10 @@ static bool CanDeclareSpecialMemberFunction(ASTContext &Context, void Sema::ForceDeclarationOfImplicitMembers(CXXRecordDecl *Class) { if (!CanDeclareSpecialMemberFunction(Context, Class)) return; + + // If the default constructor has not yet been declared, do so now. + if (!Class->hasDeclaredDefaultConstructor()) + DeclareImplicitDefaultConstructor(Class); // If the copy constructor has not yet been declared, do so now. if (!Class->hasDeclaredCopyConstructor()) @@ -509,9 +513,14 @@ static void DeclareImplicitMemberFunctionsWithName(Sema &S, switch (Name.getNameKind()) { case DeclarationName::CXXConstructorName: if (const CXXRecordDecl *Record = dyn_cast(DC)) - if (Record->getDefinition() && !Record->hasDeclaredCopyConstructor() && - CanDeclareSpecialMemberFunction(S.Context, Record)) - S.DeclareImplicitCopyConstructor(const_cast(Record)); + if (Record->getDefinition() && + CanDeclareSpecialMemberFunction(S.Context, Record)) { + if (!Record->hasDeclaredDefaultConstructor()) + S.DeclareImplicitDefaultConstructor( + const_cast(Record)); + if (!Record->hasDeclaredCopyConstructor()) + S.DeclareImplicitCopyConstructor(const_cast(Record)); + } break; case DeclarationName::CXXDestructorName: @@ -2005,9 +2014,12 @@ void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, /// \brief Look up the constructors for the given class. DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { // If the copy constructor has not yet been declared, do so now. - if (CanDeclareSpecialMemberFunction(Context, Class) && - !Class->hasDeclaredCopyConstructor()) - DeclareImplicitCopyConstructor(Class); + if (CanDeclareSpecialMemberFunction(Context, Class)) { + if (!Class->hasDeclaredDefaultConstructor()) + DeclareImplicitDefaultConstructor(Class); + if (!Class->hasDeclaredCopyConstructor()) + DeclareImplicitCopyConstructor(Class); + } CanQualType T = Context.getCanonicalType(Context.getTypeDeclType(Class)); DeclarationName Name = Context.DeclarationNames.getCXXConstructorName(T); -- 2.40.0