diff --git a/clang/include/clang/Frontend/PCHReader.h b/clang/include/clang/Frontend/PCHReader.h index 461d254817d5..41322061b172 100644 --- a/clang/include/clang/Frontend/PCHReader.h +++ b/clang/include/clang/Frontend/PCHReader.h @@ -65,6 +65,15 @@ class SwitchCase; class PCHReader; struct HeaderFileInfo; +struct PCHPredefinesBlock { + /// \brief The file ID for this predefines buffer in a PCH file. + FileID BufferID; + + /// \brief This predefines buffer in a PCH file. + llvm::StringRef Data; +}; +typedef llvm::SmallVector PCHPredefinesBlocks; + /// \brief Abstract interface for callback invocations by the PCHReader. /// /// While reading a PCH file, the PCHReader will call the methods of the @@ -103,8 +112,7 @@ public: /// here. /// /// \returns true to indicate the predefines are invalid or false otherwise. - virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, - FileID PCHBufferID, + virtual bool ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers, llvm::StringRef OriginalFileName, std::string &SuggestedPredefines) { return false; @@ -131,8 +139,7 @@ public: virtual bool ReadLanguageOptions(const LangOptions &LangOpts); virtual bool ReadTargetTriple(llvm::StringRef Triple); - virtual bool ReadPredefinesBuffer(llvm::StringRef PCHPredef, - FileID PCHBufferID, + virtual bool ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers, llvm::StringRef OriginalFileName, std::string &SuggestedPredefines); virtual void ReadHeaderFileInfo(const HeaderFileInfo &HFI, unsigned ID); @@ -483,15 +490,9 @@ private: ~ReadingKindTracker() { Reader.ReadingKind = PrevKind; } }; - /// \brief The file ID for the predefines buffer in the PCH file. - FileID PCHPredefinesBufferID; - - /// \brief Pointer to the beginning of the predefines buffer in the - /// PCH file. - const char *PCHPredefines; - - /// \brief Length of the predefines buffer in the PCH file. - unsigned PCHPredefinesLen; + /// \brief All predefines buffers in all PCH files, to be treated as if + /// concatenated. + PCHPredefinesBlocks PCHPredefinesBuffers; /// \brief Suggested contents of the predefines buffer, after this /// PCH file has been processed. @@ -509,7 +510,7 @@ private: void MaybeAddSystemRootToFilename(std::string &Filename); PCHReadResult ReadPCHBlock(); - bool CheckPredefinesBuffer(llvm::StringRef PCHPredef, FileID PCHBufferID); + bool CheckPredefinesBuffers(); bool ParseLineTable(llvm::SmallVectorImpl &Record); PCHReadResult ReadSourceManagerBlock(); PCHReadResult ReadSLocEntryRecord(unsigned ID); diff --git a/clang/lib/Frontend/PCHReader.cpp b/clang/lib/Frontend/PCHReader.cpp index b452a0d30145..cb0be410b5a4 100644 --- a/clang/lib/Frontend/PCHReader.cpp +++ b/clang/lib/Frontend/PCHReader.cpp @@ -140,8 +140,86 @@ bool PCHValidator::ReadTargetTriple(llvm::StringRef Triple) { return true; } -bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef, - FileID PCHBufferID, +struct EmptyStringRef { + bool operator ()(const llvm::StringRef &r) const { return r.empty(); } +}; +struct EmptyBlock { + bool operator ()(const PCHPredefinesBlock &r) const { return r.Data.empty(); } +}; + +static bool EqualConcatenations(llvm::SmallVector L, + PCHPredefinesBlocks R) { + // First, sum up the lengths. + unsigned LL = 0, RL = 0; + for (unsigned I = 0, N = L.size(); I != N; ++I) { + LL += L[I].size(); + } + for (unsigned I = 0, N = R.size(); I != N; ++I) { + RL += R[I].Data.size(); + } + if (LL != RL) + return false; + if (LL == 0 && RL == 0) + return true; + + // Kick out empty parts, they confuse the algorithm below. + L.erase(std::remove_if(L.begin(), L.end(), EmptyStringRef()), L.end()); + R.erase(std::remove_if(R.begin(), R.end(), EmptyBlock()), R.end()); + + // Do it the hard way. At this point, both vectors must be non-empty. + llvm::StringRef LR = L[0], RR = R[0].Data; + unsigned LI = 0, RI = 0, LN = L.size(), RN = R.size(); + for (;;) { + // Compare the current pieces. + if (LR.size() == RR.size()) { + // If they're the same length, it's pretty easy. + if (LR != RR) + return false; + // Both pieces are done, advance. + ++LI; + ++RI; + // If either string is done, they're both done, since they're the same + // length. + if (LI == LN) { + assert(RI == RN && "Strings not the same length after all?"); + return true; + } + LR = L[LI]; + RR = R[RI].Data; + } else if (LR.size() < RR.size()) { + // Right piece is longer. + if (!RR.startswith(LR)) + return false; + ++LI; + assert(LI != LN && "Strings not the same length after all?"); + RR = RR.substr(LR.size()); + LR = L[LI]; + } else { + // Left piece is longer. + if (!LR.startswith(RR)) + return false; + ++RI; + assert(RI != RN && "Strings not the same length after all?"); + LR = LR.substr(RR.size()); + RR = R[RI].Data; + } + } +} + +static std::pair +FindMacro(const PCHPredefinesBlocks &Buffers, llvm::StringRef MacroDef) { + std::pair Res; + for (unsigned I = 0, N = Buffers.size(); I != N; ++I) { + Res.second = Buffers[I].Data.find(MacroDef); + if (Res.second != llvm::StringRef::npos) { + Res.first = Buffers[I].BufferID; + break; + } + } + return Res; +} + +bool PCHValidator::ReadPredefinesBuffer(const PCHPredefinesBlocks &Buffers, llvm::StringRef OriginalFileName, std::string &SuggestedPredefines) { // We are in the context of an implicit include, so the predefines buffer will @@ -160,9 +238,15 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef, return true; } - // If the predefines is equal to the joined left and right halves, we're done! - if (Left.size() + Right.size() == PCHPredef.size() && - PCHPredef.startswith(Left) && PCHPredef.endswith(Right)) + // If the concatenation of all the PCH buffers is equal to the adjusted + // command line, we're done. + // We build a SmallVector of the command line here, because we'll eventually + // need to support an arbitrary amount of pieces anyway (when we have chained + // PCH reading). + llvm::SmallVector CommandLine; + CommandLine.push_back(Left); + CommandLine.push_back(Right); + if (EqualConcatenations(CommandLine, Buffers)) return false; SourceManager &SourceMgr = PP.getSourceManager(); @@ -170,7 +254,8 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef, // The predefines buffers are different. Determine what the differences are, // and whether they require us to reject the PCH file. llvm::SmallVector PCHLines; - PCHPredef.split(PCHLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + for (unsigned I = 0, N = Buffers.size(); I != N; ++I) + Buffers[I].Data.split(PCHLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); llvm::SmallVector CmdLineLines; Left.split(CmdLineLines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); @@ -235,10 +320,12 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef, << MacroName; // Show the definition of this macro within the PCH file. - llvm::StringRef::size_type Offset = PCHPredef.find(Missing); - assert(Offset != llvm::StringRef::npos && "Unable to find macro!"); - SourceLocation PCHMissingLoc = SourceMgr.getLocForStartOfFile(PCHBufferID) - .getFileLocWithOffset(Offset); + std::pair MacroLoc = + FindMacro(Buffers, Missing); + assert(MacroLoc.second!=llvm::StringRef::npos && "Unable to find macro!"); + SourceLocation PCHMissingLoc = + SourceMgr.getLocForStartOfFile(MacroLoc.first) + .getFileLocWithOffset(MacroLoc.second); Reader.Diag(PCHMissingLoc, diag::note_pch_macro_defined_as) << MacroName; ConflictingDefines = true; @@ -256,10 +343,12 @@ bool PCHValidator::ReadPredefinesBuffer(llvm::StringRef PCHPredef, } // Show the definition of this macro within the PCH file. - llvm::StringRef::size_type Offset = PCHPredef.find(Missing); - assert(Offset != llvm::StringRef::npos && "Unable to find macro!"); - SourceLocation PCHMissingLoc = SourceMgr.getLocForStartOfFile(PCHBufferID) - .getFileLocWithOffset(Offset); + std::pair MacroLoc = + FindMacro(Buffers, Missing); + assert(MacroLoc.second!=llvm::StringRef::npos && "Unable to find macro!"); + SourceLocation PCHMissingLoc = + SourceMgr.getLocForStartOfFile(MacroLoc.first) + .getFileLocWithOffset(MacroLoc.second); Reader.Diag(PCHMissingLoc, diag::note_using_macro_def_from_pch); } @@ -609,27 +698,18 @@ void PCHReader::Error(const char *Msg) { Diag(diag::err_fe_pch_malformed) << Msg; } -/// \brief Check the contents of the predefines buffer against the -/// contents of the predefines buffer used to build the PCH file. +/// \brief Check the contents of the concatenation of all predefines buffers in +/// the PCH chain against the contents of the predefines buffer of the current +/// compiler invocation. /// -/// The contents of the two predefines buffers should be the same. If -/// not, then some command-line option changed the preprocessor state -/// and we must reject the PCH file. -/// -/// \param PCHPredef The start of the predefines buffer in the PCH -/// file. -/// -/// \param PCHPredefLen The length of the predefines buffer in the PCH -/// file. -/// -/// \param PCHBufferID The FileID for the PCH predefines buffer. +/// The contents should be the same. If not, then some command-line option +/// changed the preprocessor state and we must probably reject the PCH file. /// /// \returns true if there was a mismatch (in which case the PCH file /// should be ignored), or false otherwise. -bool PCHReader::CheckPredefinesBuffer(llvm::StringRef PCHPredef, - FileID PCHBufferID) { +bool PCHReader::CheckPredefinesBuffers() { if (Listener) - return Listener->ReadPredefinesBuffer(PCHPredef, PCHBufferID, + return Listener->ReadPredefinesBuffer(PCHPredefinesBuffers, ActualOriginalFileName, SuggestedPredefines); return false; @@ -958,9 +1038,11 @@ PCHReader::PCHReadResult PCHReader::ReadSLocEntryRecord(unsigned ID) { FileID BufferID = SourceMgr.createFileIDForMemBuffer(Buffer, ID, Offset); if (strcmp(Name, "") == 0) { - PCHPredefinesBufferID = BufferID; - PCHPredefines = BlobStart; - PCHPredefinesLen = BlobLen - 1; + PCHPredefinesBlock Block = { + BufferID, + llvm::StringRef(BlobStart, BlobLen - 1) + }; + PCHPredefinesBuffers.push_back(Block); } break; @@ -1636,8 +1718,7 @@ PCHReader::PCHReadResult PCHReader::ReadPCH(const std::string &FileName) { } // Check the predefines buffer. - if (CheckPredefinesBuffer(llvm::StringRef(PCHPredefines, PCHPredefinesLen), - PCHPredefinesBufferID)) + if (CheckPredefinesBuffers()) return IgnorePCH; if (PP) { diff --git a/clang/test/PCH/pchpch.c b/clang/test/PCH/pchpch.c new file mode 100644 index 000000000000..d68a6ad4aee6 --- /dev/null +++ b/clang/test/PCH/pchpch.c @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-pch -o %t1 %S/pchpch1.h +// RUN: %clang_cc1 -triple i386-unknown-unknown -emit-pch -o %t2 %S/pchpch2.h -include-pch %t1 +// RUN: %clang_cc1 -triple i386-unknown-unknown -fsyntax-only %s -include-pch %t2 + +// The purpose of this test is to make sure that a PCH created while including +// an existing PCH can be loaded. diff --git a/clang/test/PCH/pchpch1.h b/clang/test/PCH/pchpch1.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/clang/test/PCH/pchpch2.h b/clang/test/PCH/pchpch2.h new file mode 100644 index 000000000000..e69de29bb2d1