diff --git a/clang/include/clang/AST/Redeclarable.h b/clang/include/clang/AST/Redeclarable.h index 55e1f8477992..85c75b771b65 100644 --- a/clang/include/clang/AST/Redeclarable.h +++ b/clang/include/clang/AST/Redeclarable.h @@ -177,6 +177,9 @@ public: static_cast(this))); } redecl_iterator redecls_end() const { return redecl_iterator(); } + + friend class PCHDeclReader; + friend class PCHDeclWriter; }; } diff --git a/clang/include/clang/Frontend/PCHBitCodes.h b/clang/include/clang/Frontend/PCHBitCodes.h index 109a78bd8849..a07060f8001f 100644 --- a/clang/include/clang/Frontend/PCHBitCodes.h +++ b/clang/include/clang/Frontend/PCHBitCodes.h @@ -245,8 +245,12 @@ namespace clang { /// declarations. TU_UPDATE_LEXICAL = 28, + /// \brief Record code for an update to first decls pointing to the + /// latest redeclarations. + REDECLS_UPDATE_LATEST = 29, + /// \brief Record code for declarations that Sema keeps references of. - SEMA_DECL_REFS = 29 + SEMA_DECL_REFS = 30 }; /// \brief Record types used within a source manager block. diff --git a/clang/include/clang/Frontend/PCHReader.h b/clang/include/clang/Frontend/PCHReader.h index dda15670b5e9..e19669ec7f40 100644 --- a/clang/include/clang/Frontend/PCHReader.h +++ b/clang/include/clang/Frontend/PCHReader.h @@ -319,6 +319,11 @@ private: /// DeclContext. DeclContextOffsetsMap DeclContextOffsets; + typedef llvm::DenseMap FirstLatestDeclIDMap; + /// \brief Map of first declarations from a chained PCH that point to the + /// most recent declarations in another PCH. + FirstLatestDeclIDMap FirstLatestDeclIDs; + /// \brief Read the records that describe the contents of declcontexts. bool ReadDeclContextStorage(llvm::BitstreamCursor &Cursor, const std::pair &Offsets, @@ -561,7 +566,7 @@ private: QualType ReadTypeRecord(unsigned Index); RecordLocation TypeCursorForIndex(unsigned Index); void LoadedDecl(unsigned Index, Decl *D); - Decl *ReadDeclRecord(unsigned Index); + Decl *ReadDeclRecord(unsigned Index, pch::DeclID ID); RecordLocation DeclCursorForIndex(unsigned Index); void PassInterestingDeclsToConsumer(); diff --git a/clang/include/clang/Frontend/PCHWriter.h b/clang/include/clang/Frontend/PCHWriter.h index 92c7085bc6fc..3403c6e2d177 100644 --- a/clang/include/clang/Frontend/PCHWriter.h +++ b/clang/include/clang/Frontend/PCHWriter.h @@ -79,6 +79,7 @@ class PCHWriter : public PCHDeserializationListener { public: typedef llvm::SmallVector RecordData; + friend class PCHDeclWriter; private: /// \brief The bitstream writer used to emit this precompiled header. llvm::BitstreamWriter &Stream; @@ -195,6 +196,11 @@ private: /// \brief Mapping from the macro definition indices in \c MacroDefinitions /// to the corresponding offsets within the preprocessor block. std::vector MacroDefinitionOffsets; + + typedef llvm::DenseMap FirstLatestDeclMap; + /// \brief Map of first declarations from a chained PCH that point to the + /// most recent declarations in another PCH. + FirstLatestDeclMap FirstLatestDecls; /// \brief Declarations encountered that might be external /// definitions. diff --git a/clang/lib/Frontend/PCHReader.cpp b/clang/lib/Frontend/PCHReader.cpp index b415f8dea540..35909a055566 100644 --- a/clang/lib/Frontend/PCHReader.cpp +++ b/clang/lib/Frontend/PCHReader.cpp @@ -1557,6 +1557,18 @@ PCHReader::ReadPCHBlock(PerFileData &F) { break; } + case pch::REDECLS_UPDATE_LATEST: { + assert(Record.size() % 2 == 0 && "Expected pairs of DeclIDs"); + for (unsigned i = 0, e = Record.size(); i < e; i += 2) { + pch::DeclID First = Record[i], Latest = Record[i+1]; + assert((FirstLatestDeclIDs.find(First) == FirstLatestDeclIDs.end() || + Latest > FirstLatestDeclIDs[First]) && + "The new latest is supposed to come after the previous latest"); + FirstLatestDeclIDs[First] = Latest; + } + break; + } + case pch::LANGUAGE_OPTIONS: if (ParseLanguageOptions(Record) && !DisableValidation) return IgnorePCH; @@ -2868,7 +2880,7 @@ Decl *PCHReader::GetExternalDecl(uint32_t ID) { TranslationUnitDecl *PCHReader::GetTranslationUnitDecl() { if (!DeclsLoaded[0]) { - ReadDeclRecord(0); + ReadDeclRecord(0, 0); if (DeserializationListener) DeserializationListener->DeclRead(1, DeclsLoaded[0]); } @@ -2887,7 +2899,7 @@ Decl *PCHReader::GetDecl(pch::DeclID ID) { unsigned Index = ID - 1; if (!DeclsLoaded[Index]) { - ReadDeclRecord(Index); + ReadDeclRecord(Index, ID); if (DeserializationListener) DeserializationListener->DeclRead(ID, DeclsLoaded[Index]); } diff --git a/clang/lib/Frontend/PCHReaderDecl.cpp b/clang/lib/Frontend/PCHReaderDecl.cpp index e9428cc0b4bd..21beb42e93e5 100644 --- a/clang/lib/Frontend/PCHReaderDecl.cpp +++ b/clang/lib/Frontend/PCHReaderDecl.cpp @@ -31,6 +31,7 @@ namespace clang { class PCHDeclReader : public DeclVisitor { PCHReader &Reader; llvm::BitstreamCursor &Cursor; + const pch::DeclID ThisDeclID; const PCHReader::RecordData &Record; unsigned &Idx; pch::TypeID TypeIDForTypeDecl; @@ -39,9 +40,10 @@ namespace clang { public: PCHDeclReader(PCHReader &Reader, llvm::BitstreamCursor &Cursor, - const PCHReader::RecordData &Record, unsigned &Idx) - : Reader(Reader), Cursor(Cursor), Record(Record), Idx(Idx), - TypeIDForTypeDecl(0) { } + pch::DeclID thisDeclID, const PCHReader::RecordData &Record, + unsigned &Idx) + : Reader(Reader), Cursor(Cursor), ThisDeclID(thisDeclID), Record(Record), + Idx(Idx), TypeIDForTypeDecl(0) { } void Visit(Decl *D); @@ -93,6 +95,7 @@ namespace clang { void VisitBlockDecl(BlockDecl *BD); std::pair VisitDeclContext(DeclContext *DC); + template void VisitRedeclarable(Redeclarable *D); // FIXME: Reorder according to DeclNodes.td? void VisitObjCMethodDecl(ObjCMethodDecl *D); @@ -178,8 +181,7 @@ void PCHDeclReader::VisitTypedefDecl(TypedefDecl *TD) { void PCHDeclReader::VisitTagDecl(TagDecl *TD) { VisitTypeDecl(TD); TD->IdentifierNamespace = Record[Idx++]; - TD->setPreviousDeclaration( - cast_or_null(Reader.GetDecl(Record[Idx++]))); + VisitRedeclarable(TD); TD->setTagKind((TagDecl::TagKind)Record[Idx++]); TD->setDefinition(Record[Idx++]); TD->setEmbeddedInDeclarator(Record[Idx++]); @@ -305,10 +307,7 @@ void PCHDeclReader::VisitFunctionDecl(FunctionDecl *FD) { // FunctionDecl's body is handled last at PCHReaderDecl::Visit, // after everything else is read. - // Avoid side effects and invariant checking of FunctionDecl's - // setPreviousDeclaration. - FD->redeclarable_base::setPreviousDeclaration( - cast_or_null(Reader.GetDecl(Record[Idx++]))); + VisitRedeclarable(FD); FD->setStorageClass((FunctionDecl::StorageClass)Record[Idx++]); FD->setStorageClassAsWritten((FunctionDecl::StorageClass)Record[Idx++]); FD->setInlineSpecified(Record[Idx++]); @@ -550,8 +549,7 @@ void PCHDeclReader::VisitVarDecl(VarDecl *VD) { VD->setDeclaredInCondition(Record[Idx++]); VD->setExceptionVariable(Record[Idx++]); VD->setNRVOVariable(Record[Idx++]); - VD->setPreviousDeclaration( - cast_or_null(Reader.GetDecl(Record[Idx++]))); + VisitRedeclarable(VD); if (Record[Idx++]) VD->setInit(Reader.ReadExpr(Cursor)); @@ -918,6 +916,25 @@ void PCHDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclarableTemplateDecl *LatestDecl = cast_or_null(Reader.GetDecl(Record[Idx++])); + + // This decl is a first one and the latest declaration that it points to is + // in the same PCH. However, if this actually needs to point to a + // redeclaration in another chained PCH, we need to update it by checking + // the FirstLatestDeclIDs map which tracks this kind of decls. + assert(Reader.GetDecl(ThisDeclID) == D && "Invalid ThisDeclID ?"); + PCHReader::FirstLatestDeclIDMap::iterator I + = Reader.FirstLatestDeclIDs.find(ThisDeclID); + if (I != Reader.FirstLatestDeclIDs.end()) { + Decl *NewLatest = Reader.GetDecl(I->second); + assert((LatestDecl->getLocation().isInvalid() || + NewLatest->getLocation().isInvalid() || + Reader.SourceMgr.isBeforeInTranslationUnit( + LatestDecl->getLocation(), + NewLatest->getLocation())) && + "The new latest is supposed to come after the previous latest"); + LatestDecl = cast(NewLatest); + } + assert(LatestDecl->getKind() == D->getKind() && "Latest kind mismatch"); D->getCommonPtr()->Latest = LatestDecl; } @@ -1072,6 +1089,54 @@ PCHDeclReader::VisitDeclContext(DeclContext *DC) { return std::make_pair(LexicalOffset, VisibleOffset); } +template +void PCHDeclReader::VisitRedeclarable(Redeclarable *D) { + enum RedeclKind { NoRedeclaration = 0, PointsToPrevious, PointsToLatest }; + RedeclKind Kind = (RedeclKind)Record[Idx++]; + switch (Kind) { + default: + assert(0 && "Out of sync with PCHDeclWriter::VisitRedeclarable or messed up" + " reading"); + case NoRedeclaration: + break; + case PointsToPrevious: + D->RedeclLink = typename Redeclarable::PreviousDeclLink( + cast_or_null(Reader.GetDecl(Record[Idx++]))); + break; + case PointsToLatest: + D->RedeclLink = typename Redeclarable::LatestDeclLink( + cast_or_null(Reader.GetDecl(Record[Idx++]))); + break; + } + + assert(!(Kind == PointsToPrevious && + Reader.FirstLatestDeclIDs.find(ThisDeclID) != + Reader.FirstLatestDeclIDs.end()) && + "This decl is not first, it should not be in the map"); + if (Kind == PointsToPrevious) + return; + + // This decl is a first one and the latest declaration that it points to is in + // the same PCH. However, if this actually needs to point to a redeclaration + // in another chained PCH, we need to update it by checking the + // FirstLatestDeclIDs map which tracks this kind of decls. + assert(Reader.GetDecl(ThisDeclID) == static_cast(D) && + "Invalid ThisDeclID ?"); + PCHReader::FirstLatestDeclIDMap::iterator I + = Reader.FirstLatestDeclIDs.find(ThisDeclID); + if (I != Reader.FirstLatestDeclIDs.end()) { + Decl *NewLatest = Reader.GetDecl(I->second); + assert((D->getMostRecentDeclaration()->getLocation().isInvalid() || + NewLatest->getLocation().isInvalid() || + Reader.SourceMgr.isBeforeInTranslationUnit( + D->getMostRecentDeclaration()->getLocation(), + NewLatest->getLocation())) && + "The new latest is supposed to come after the previous latest"); + D->RedeclLink + = typename Redeclarable::LatestDeclLink(cast_or_null(NewLatest)); + } +} + //===----------------------------------------------------------------------===// // Attribute Reading //===----------------------------------------------------------------------===// @@ -1304,7 +1369,7 @@ PCHReader::RecordLocation PCHReader::DeclCursorForIndex(unsigned Index) { } /// \brief Read the declaration at the given offset from the PCH file. -Decl *PCHReader::ReadDeclRecord(unsigned Index) { +Decl *PCHReader::ReadDeclRecord(unsigned Index, pch::DeclID ID) { RecordLocation Loc = DeclCursorForIndex(Index); llvm::BitstreamCursor &DeclsCursor = *Loc.first; // Keep track of where we are in the stream, then jump back there @@ -1320,7 +1385,7 @@ Decl *PCHReader::ReadDeclRecord(unsigned Index) { RecordData Record; unsigned Code = DeclsCursor.ReadCode(); unsigned Idx = 0; - PCHDeclReader Reader(*this, DeclsCursor, Record, Idx); + PCHDeclReader Reader(*this, DeclsCursor, ID, Record, Idx); Decl *D = 0; switch ((pch::DeclCode)DeclsCursor.ReadRecord(Code, Record)) { diff --git a/clang/lib/Frontend/PCHWriter.cpp b/clang/lib/Frontend/PCHWriter.cpp index 75592a41c858..776a40940d76 100644 --- a/clang/lib/Frontend/PCHWriter.cpp +++ b/clang/lib/Frontend/PCHWriter.cpp @@ -2410,6 +2410,19 @@ void PCHWriter::WritePCHChain(Sema &SemaRef, MemorizeStatCalls *StatCalls, WriteIdentifierTable(PP); WriteTypeDeclOffsets(); + /// Build a record containing first declarations from a chained PCH and the + /// most recent declarations in this PCH that they point to. + RecordData FirstLatestDeclIDs; + for (FirstLatestDeclMap::iterator + I = FirstLatestDecls.begin(), E = FirstLatestDecls.end(); I != E; ++I) { + assert(I->first->getPCHLevel() > I->second->getPCHLevel() && + "Expected first & second to be in different PCHs"); + AddDeclRef(I->first, FirstLatestDeclIDs); + AddDeclRef(I->second, FirstLatestDeclIDs); + } + if (!FirstLatestDeclIDs.empty()) + Stream.EmitRecord(pch::REDECLS_UPDATE_LATEST, FirstLatestDeclIDs); + // Write the record containing external, unnamed definitions. if (!ExternalDefinitions.empty()) Stream.EmitRecord(pch::EXTERNAL_DEFINITIONS, ExternalDefinitions); diff --git a/clang/lib/Frontend/PCHWriterDecl.cpp b/clang/lib/Frontend/PCHWriterDecl.cpp index 28a82cd23f5b..9e9835adb424 100644 --- a/clang/lib/Frontend/PCHWriterDecl.cpp +++ b/clang/lib/Frontend/PCHWriterDecl.cpp @@ -92,6 +92,7 @@ namespace clang { void VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset, uint64_t VisibleOffset); + template void VisitRedeclarable(Redeclarable *D); // FIXME: Put in the same order is DeclNodes.td? @@ -163,7 +164,7 @@ void PCHDeclWriter::VisitTypedefDecl(TypedefDecl *D) { void PCHDeclWriter::VisitTagDecl(TagDecl *D) { VisitTypeDecl(D); Record.push_back(D->getIdentifierNamespace()); - Writer.AddDeclRef(D->getPreviousDeclaration(), Record); + VisitRedeclarable(D); Record.push_back((unsigned)D->getTagKind()); // FIXME: stable encoding Record.push_back(D->isDefinition()); Record.push_back(D->isEmbeddedInDeclarator()); @@ -279,7 +280,7 @@ void PCHDeclWriter::VisitFunctionDecl(FunctionDecl *D) { // FunctionDecl's body is handled last at PCHWriterDecl::Visit, // after everything else is written. - Writer.AddDeclRef(D->getPreviousDeclaration(), Record); + VisitRedeclarable(D); Record.push_back(D->getStorageClass()); // FIXME: stable encoding Record.push_back(D->getStorageClassAsWritten()); Record.push_back(D->isInlineSpecified()); @@ -500,7 +501,7 @@ void PCHDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(D->isDeclaredInCondition()); Record.push_back(D->isExceptionVariable()); Record.push_back(D->isNRVOVariable()); - Writer.AddDeclRef(D->getPreviousDeclaration(), Record); + VisitRedeclarable(D); Record.push_back(D->getInit() ? 1 : 0); if (D->getInit()) Writer.AddStmt(D->getInit()); @@ -854,6 +855,18 @@ void PCHDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { Record.push_back(D->isMemberSpecialization()); Writer.AddDeclRef(D->getCommonPtr()->Latest, Record); + } else { + RedeclarableTemplateDecl *First = D->getFirstDeclaration(); + assert(First != D); + // If this is a most recent redeclaration that is pointed to by a first decl + // in a chained PCH, keep track of the association with the map so we can + // update the first decl during PCH reading. + if (First->getMostRecentDeclaration() == D && + First->getPCHLevel() > D->getPCHLevel()) { + assert(Writer.FirstLatestDecls.find(First)==Writer.FirstLatestDecls.end() + && "The latest is already set"); + Writer.FirstLatestDecls[First] = D; + } } } @@ -1016,6 +1029,29 @@ void PCHDeclWriter::VisitDeclContext(DeclContext *DC, uint64_t LexicalOffset, Record.push_back(VisibleOffset); } +template +void PCHDeclWriter::VisitRedeclarable(Redeclarable *D) { + enum { NoRedeclaration = 0, PointsToPrevious, PointsToLatest }; + if (D->RedeclLink.getNext() == D) { + Record.push_back(NoRedeclaration); + } else { + Record.push_back(D->RedeclLink.NextIsPrevious() ? PointsToPrevious + : PointsToLatest); + Writer.AddDeclRef(D->RedeclLink.getPointer(), Record); + } + + T *First = D->getFirstDeclaration(); + T *ThisDecl = static_cast(D); + // If this is a most recent redeclaration that is pointed to by a first decl + // in a chained PCH, keep track of the association with the map so we can + // update the first decl during PCH reading. + if (ThisDecl != First && First->getMostRecentDeclaration() == ThisDecl && + First->getPCHLevel() > ThisDecl->getPCHLevel()) { + assert(Writer.FirstLatestDecls.find(First) == Writer.FirstLatestDecls.end() + && "The latest is already set"); + Writer.FirstLatestDecls[First] = ThisDecl; + } +} //===----------------------------------------------------------------------===// // PCHWriter Implementation diff --git a/clang/test/PCH/cxx-templates.cpp b/clang/test/PCH/cxx-templates.cpp index f12742755a9f..8e89b1da462b 100644 --- a/clang/test/PCH/cxx-templates.cpp +++ b/clang/test/PCH/cxx-templates.cpp @@ -1,9 +1,13 @@ // Test this without pch. -// RUN: %clang_cc1 -include %S/cxx-templates.h -verify %s -ast-dump +// RUN: %clang_cc1 -include %S/cxx-templates.h -verify %s -ast-dump 1>/dev/null +// RUN: %clang_cc1 -include %S/cxx-templates.h %s -emit-llvm -o - | FileCheck %s // Test with pch. // RUN: %clang_cc1 -x c++-header -emit-pch -o %t %S/cxx-templates.h -// RUN: %clang_cc1 -include-pch %t -verify %s -ast-dump +// RUN: %clang_cc1 -include-pch %t -verify %s -ast-dump 1>/dev/null +// RUN: %clang_cc1 -include-pch %t %s -emit-llvm -o - | FileCheck %s + +// CHECK: define linkonce_odr void @_ZN2S3IiE1mEv struct A { typedef int type; @@ -22,4 +26,7 @@ void test() { Dep::Ty ty; Dep a; a.f(); + + S3 s3; + s3.m(); } diff --git a/clang/test/PCH/cxx-templates.h b/clang/test/PCH/cxx-templates.h index 92932199fa82..91d53d3060a0 100644 --- a/clang/test/PCH/cxx-templates.h +++ b/clang/test/PCH/cxx-templates.h @@ -116,3 +116,11 @@ struct S2 { }; extern template class S2; + +template +struct S3 { + void m(); +}; + +template +inline void S3::m() { }