From 5456b0fe40714a78cd0ba7c1a5b7dc34eda385af Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Tue, 9 Oct 2012 17:21:28 +0000 Subject: [PATCH] When we load a function or method body from an AST file, we check whether that function/method already has a body (loaded from some other AST file), as introduced in r165137. Delay this check until after the redeclaration chains have been wired up. While I'm here, make the loading of method bodies lazy. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@165513 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclObjC.h | 18 ++++++++------ include/clang/Serialization/ASTReader.h | 5 +++- lib/AST/DeclObjC.cpp | 4 +++ lib/Serialization/ASTReader.cpp | 22 ++++++++++++++++- lib/Serialization/ASTReaderDecl.cpp | 31 +++++++++++++----------- test/Modules/Inputs/redecl-merge-left.h | 2 +- test/Modules/Inputs/redecl-merge-right.h | 2 +- test/Modules/Inputs/redecl-merge-top.h | 2 ++ 8 files changed, 61 insertions(+), 25 deletions(-) diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index a1a287118e..459e9b3500 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -174,8 +174,7 @@ private: SourceLocation DeclEndLoc; // the location of the ';' or '{'. // The following are only used for method definitions, null otherwise. - // FIXME: space savings opportunity, consider a sub-class. - Stmt *Body; + LazyDeclStmtPtr Body; /// SelfDecl - Decl for the implicit self parameter. This is lazily /// constructed by createImplicitParams. @@ -242,7 +241,7 @@ private: SelLocsKind(SelLoc_StandardNoSpace), IsOverriding(0), MethodDeclType(T), ResultTInfo(ResultTInfo), ParamsAndSelLocs(0), NumParams(0), - DeclEndLoc(endLoc), Body(0), SelfDecl(0), CmdDecl(0) { + DeclEndLoc(endLoc), Body(), SelfDecl(0), CmdDecl(0) { setImplicit(isImplicitlyDeclared); } @@ -427,10 +426,15 @@ public: return ImplementationControl(DeclImplementation); } - virtual Stmt *getBody() const { - return (Stmt*) Body; - } - CompoundStmt *getCompoundBody() { return (CompoundStmt*)Body; } + /// \brief Determine whether this method has a body. + virtual bool hasBody() const { return Body; } + + /// \brief Retrieve the body of this method, if it has one. + virtual Stmt *getBody() const; + + void setLazyBody(uint64_t Offset) { Body = Offset; } + + CompoundStmt *getCompoundBody() { return (CompoundStmt*)getBody(); } void setBody(Stmt *B) { Body = B; } /// \brief Returns whether this specific method is a definition. diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 7bdd0a70dc..5136113f79 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -341,7 +341,10 @@ private: /// \brief The set of C++ or Objective-C classes that have forward /// declarations that have not yet been linked to their definitions. llvm::SmallPtrSet PendingDefinitions; - + + /// \brief Functions or methods that have bodies that will be attached. + llvm::SmallDenseMap PendingBodies; + /// \brief Read the records that describe the contents of declcontexts. bool ReadDeclContextStorage(ModuleFile &M, llvm::BitstreamCursor &Cursor, diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index d28a910d0a..45c57d6591 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -445,6 +445,10 @@ ObjCMethodDecl *ObjCMethodDecl::CreateDeserialized(ASTContext &C, unsigned ID) { Selector(), QualType(), 0, 0); } +Stmt *ObjCMethodDecl::getBody() const { + return Body.get(getASTContext().getExternalSource()); +} + void ObjCMethodDecl::setAsRedeclaration(const ObjCMethodDecl *PrevMethod) { assert(PrevMethod); getASTContext().setObjCMethodRedeclaration(PrevMethod, this); diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 05a453143c..b37f8c2363 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -6477,9 +6477,29 @@ void ASTReader::finishPendingActions() { for (RedeclarableTemplateDecl::redecl_iterator R = RTD->redecls_begin(), REnd = RTD->redecls_end(); R != REnd; ++R) - R->Common = RTD->Common; + R->Common = RTD->Common; } PendingDefinitions.clear(); + + // Load the bodies of any functions or methods we've encountered. We do + // this now (delayed) so that we can be sure that the declaration chains + // have been fully wired up. + for (llvm::SmallDenseMap::iterator + PB = PendingBodies.begin(), PBEnd = PendingBodies.end(); + PB != PBEnd; ++PB) { + if (FunctionDecl *FD = dyn_cast(PB->first)) { + // FIXME: Check for =delete/=default? + // FIXME: Complain about ODR violations here? + if (!getContext().getLangOpts().Modules || !FD->hasBody()) + FD->setLazyBody(PB->second); + continue; + } + + ObjCMethodDecl *MD = cast(PB->first); + if (!getContext().getLangOpts().Modules || !MD->hasBody()) + MD->setLazyBody(PB->second); + } + PendingBodies.clear(); } void ASTReader::FinishedDeserializing() { diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 762ad01b51..41d8ff98a1 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -47,6 +47,8 @@ namespace clang { DeclID DeclContextIDForTemplateParmDecl; DeclID LexicalDeclContextIDForTemplateParmDecl; + bool HasPendingBody; + uint64_t GetCurrentCursorOffset(); SourceLocation ReadSourceLocation(const RecordData &R, unsigned &I) { @@ -198,11 +200,14 @@ namespace clang { const RecordData &Record, unsigned &Idx) : Reader(Reader), F(F), ThisDeclID(thisDeclID), RawLocation(RawLocation), Record(Record), Idx(Idx), - TypeIDForTypeDecl(0) { } + TypeIDForTypeDecl(0), HasPendingBody(false) { } static void attachPreviousDecl(Decl *D, Decl *previous); static void attachLatestDecl(Decl *D, Decl *latest); + /// \brief Determine whether this declaration has a pending body. + bool hasPendingBody() const { return HasPendingBody; } + void Visit(Decl *D); void UpdateDecl(Decl *D, ModuleFile &ModuleFile, @@ -324,9 +329,10 @@ void ASTDeclReader::Visit(Decl *D) { // module). // FIXME: Also consider = default and = delete. // FIXME: Can we diagnose ODR violations somehow? - if (Record[Idx++] && - (!Reader.getContext().getLangOpts().Modules || !FD->hasBody())) - FD->setLazyBody(GetCurrentCursorOffset()); + if (Record[Idx++]) { + Reader.PendingBodies[FD] = GetCurrentCursorOffset(); + HasPendingBody = true; + } } else if (D->isTemplateParameter()) { // If we have a fully initialized template parameter, we can now // set its DeclContext. @@ -634,13 +640,10 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) { void ASTDeclReader::VisitObjCMethodDecl(ObjCMethodDecl *MD) { VisitNamedDecl(MD); if (Record[Idx++]) { - // In practice, this won't be executed (since method definitions - // don't occur in header files). - // Switch case IDs for this method body. - ASTReader::SwitchCaseMapTy SwitchCaseStmtsForObjCMethod; - SaveAndRestore - SCFOM(Reader.CurrSwitchCaseStmts, &SwitchCaseStmtsForObjCMethod); - MD->setBody(Reader.ReadStmt(F)); + // Load the body on-demand. Most clients won't care, because method + // definitions rarely show up in headers. + Reader.PendingBodies[MD] = GetCurrentCursorOffset(); + HasPendingBody = true; MD->setSelfDecl(ReadDeclAs(Record, Idx)); MD->setCmdDecl(ReadDeclAs(Record, Idx)); } @@ -1662,7 +1665,7 @@ inline void ASTReader::LoadedDecl(unsigned Index, Decl *D) { /// This routine should return true for anything that might affect /// code generation, e.g., inline function definitions, Objective-C /// declarations with metadata, etc. -static bool isConsumerInterestedIn(Decl *D) { +static bool isConsumerInterestedIn(Decl *D, bool HasBody) { // An ObjCMethodDecl is never considered as "interesting" because its // implementation container always is. @@ -1674,7 +1677,7 @@ static bool isConsumerInterestedIn(Decl *D) { return Var->isFileVarDecl() && Var->isThisDeclarationADefinition() == VarDecl::Definition; if (FunctionDecl *Func = dyn_cast(D)) - return Func->doesThisDeclarationHaveABody(); + return Func->doesThisDeclarationHaveABody() || HasBody; return false; } @@ -2162,7 +2165,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { // AST consumer might need to know about, queue it. // We don't pass it to the consumer immediately because we may be in recursive // loading, and some declarations may still be initializing. - if (isConsumerInterestedIn(D)) + if (isConsumerInterestedIn(D, Reader.hasPendingBody())) InterestingDecls.push_back(D); return D; diff --git a/test/Modules/Inputs/redecl-merge-left.h b/test/Modules/Inputs/redecl-merge-left.h index ee794ff320..5e6d2e512b 100644 --- a/test/Modules/Inputs/redecl-merge-left.h +++ b/test/Modules/Inputs/redecl-merge-left.h @@ -60,7 +60,7 @@ typedef int T1; typedef float T2; int func0(int); -int func1(int); +int func1(int x) { return x; } int func2(int); diff --git a/test/Modules/Inputs/redecl-merge-right.h b/test/Modules/Inputs/redecl-merge-right.h index f62020f205..20223083c3 100644 --- a/test/Modules/Inputs/redecl-merge-right.h +++ b/test/Modules/Inputs/redecl-merge-right.h @@ -65,7 +65,7 @@ typedef double T2; int func0(int); int func1(int); int func1(int); -int func1(int); +int func1(int x) { return x; } int func1(int); static int func2(int); diff --git a/test/Modules/Inputs/redecl-merge-top.h b/test/Modules/Inputs/redecl-merge-top.h index 25456deb28..690e6df1c9 100644 --- a/test/Modules/Inputs/redecl-merge-top.h +++ b/test/Modules/Inputs/redecl-merge-top.h @@ -14,3 +14,5 @@ struct S1; struct S2; struct S2; + +int func1(int); -- 2.40.0