From: Douglas Gregor Date: Fri, 10 Apr 2009 23:10:45 +0000 (+0000) Subject: Compare the predefines buffer in the PCH file with the predefines X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=e1d918e9fe55e3b34401fd5d420c47ea0f9572c9;p=clang Compare the predefines buffer in the PCH file with the predefines buffer generated for the current translation unit. If they are different, complain and then ignore the PCH file. This effectively checks for all compilation options that somehow would affect preprocessor state (-D, -U, -include, the dreaded -imacros, etc.). When we do accept the PCH file, throw away the contents of the predefines buffer rather than parsing them, since all of the results of that parsing are already stored in the PCH file. This eliminates the ugliness with the redefinition of __builtin_va_list, among other things. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68838 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 9db74e2518..55d1dfacb8 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -110,4 +110,9 @@ def warn_pch_gc_mode : Warning< "the PCH file was built with %select{no||hybrid}0 garbage collection but " "the current translation unit will compiled with %select{no||hybrid}1 " "garbage collection">; +def warn_pch_preprocessor : Warning< + "the PCH file was built with different preprocessor definitions than the " + "current translation unit">; +def note_predef_in_pch : Note< + "preprocessor definitions in PCH file">; } diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h index 0c14df070e..3efb3a55d6 100644 --- a/include/clang/Frontend/PCHReader.h +++ b/include/clang/Frontend/PCHReader.h @@ -51,6 +51,10 @@ class Preprocessor; /// required when traversing the AST. Only those AST nodes that are /// actually required will be de-serialized. class PCHReader : public ExternalASTSource { +public: + enum PCHReadResult { Success, Failure, IgnorePCH }; + +private: /// \brief The preprocessor that will be loading the source file. Preprocessor &PP; @@ -103,10 +107,11 @@ class PCHReader : public ExternalASTSource { /// DeclContext. DeclContextOffsetsMap DeclContextOffsets; - enum PCHReadResult { Success, Failure, IgnorePCH }; - PCHReadResult ReadPCHBlock(); - bool ReadSourceManagerBlock(); + bool CheckPredefinesBuffer(const char *PCHPredef, + unsigned PCHPredefLen, + FileID PCHBufferID); + PCHReadResult ReadSourceManagerBlock(); bool ReadPreprocessorBlock(); bool ParseLanguageOptions(const llvm::SmallVectorImpl &Record); @@ -125,7 +130,7 @@ public: ~PCHReader(); - bool ReadPCH(const std::string &FileName); + PCHReadResult ReadPCH(const std::string &FileName); /// \brief Resolve a type ID into a type, potentially building a new /// type. @@ -176,6 +181,9 @@ public: /// \brief Report a diagnostic. DiagnosticBuilder Diag(unsigned DiagID); + /// \brief Report a diagnostic. + DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID); + const IdentifierInfo *GetIdentifierInfo(const RecordData &Record, unsigned &Idx); DeclarationName ReadDeclarationName(const RecordData &Record, unsigned &Idx); diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 656ebed899..47f40b68f6 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -788,7 +788,6 @@ class PreprocessorFactory { public: virtual ~PreprocessorFactory(); virtual Preprocessor* CreatePreprocessor() = 0; - virtual bool FinishInitialization(Preprocessor *PP, bool usesPCH); }; } // end namespace clang diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index eaba610137..345e673b47 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -116,27 +116,109 @@ static bool Error(const char *Str) { return true; } +/// \brief Check the contents of the predefines buffer against the +/// contents of the predefines buffer used to build the PCH file. +/// +/// 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. +/// +/// \returns true if there was a mismatch (in which case the PCH file +/// should be ignored), or false otherwise. +bool PCHReader::CheckPredefinesBuffer(const char *PCHPredef, + unsigned PCHPredefLen, + FileID PCHBufferID) { + const char *Predef = PP.getPredefines().c_str(); + unsigned PredefLen = PP.getPredefines().size(); + + // If the two predefines buffers compare equal, we're done!. + if (PredefLen == PCHPredefLen && + strncmp(Predef, PCHPredef, PCHPredefLen) == 0) + return false; + + // The predefines buffers are different. Produce a reasonable + // diagnostic showing where they are different. + + // The source locations (potentially in the two different predefines + // buffers) + SourceLocation Loc1, Loc2; + SourceManager &SourceMgr = PP.getSourceManager(); + + // Create a source buffer for our predefines string, so + // that we can build a diagnostic that points into that + // source buffer. + FileID BufferID; + if (Predef && Predef[0]) { + llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBuffer(Predef, Predef + PredefLen, + ""); + BufferID = SourceMgr.createFileIDForMemBuffer(Buffer); + } + + unsigned MinLen = std::min(PredefLen, PCHPredefLen); + std::pair Locations + = std::mismatch(Predef, Predef + MinLen, PCHPredef); + + if (Locations.first != Predef + MinLen) { + // We found the location in the two buffers where there is a + // difference. Form source locations to point there (in both + // buffers). + unsigned Offset = Locations.first - Predef; + Loc1 = SourceMgr.getLocForStartOfFile(BufferID) + .getFileLocWithOffset(Offset); + Loc2 = SourceMgr.getLocForStartOfFile(PCHBufferID) + .getFileLocWithOffset(Offset); + } else if (PredefLen > PCHPredefLen) { + Loc1 = SourceMgr.getLocForStartOfFile(BufferID) + .getFileLocWithOffset(MinLen); + } else { + Loc1 = SourceMgr.getLocForStartOfFile(PCHBufferID) + .getFileLocWithOffset(MinLen); + } + + Diag(Loc1, diag::warn_pch_preprocessor); + if (Loc2.isValid()) + Diag(Loc2, diag::note_predef_in_pch); + Diag(diag::note_ignoring_pch) << FileName; + return true; +} + /// \brief Read the source manager block -bool PCHReader::ReadSourceManagerBlock() { +PCHReader::PCHReadResult PCHReader::ReadSourceManagerBlock() { using namespace SrcMgr; - if (Stream.EnterSubBlock(pch::SOURCE_MANAGER_BLOCK_ID)) - return Error("Malformed source manager block record"); + if (Stream.EnterSubBlock(pch::SOURCE_MANAGER_BLOCK_ID)) { + Error("Malformed source manager block record"); + return Failure; + } SourceManager &SourceMgr = Context.getSourceManager(); RecordData Record; while (true) { unsigned Code = Stream.ReadCode(); if (Code == llvm::bitc::END_BLOCK) { - if (Stream.ReadBlockEnd()) - return Error("Error at end of Source Manager block"); - return false; + if (Stream.ReadBlockEnd()) { + Error("Error at end of Source Manager block"); + return Failure; + } + + return Success; } if (Code == llvm::bitc::ENTER_SUBBLOCK) { // No known subblocks, always skip them. Stream.ReadSubBlockID(); - if (Stream.SkipBlock()) - return Error("Malformed block record"); + if (Stream.SkipBlock()) { + Error("Malformed block record"); + return Failure; + } continue; } @@ -172,9 +254,15 @@ bool PCHReader::ReadSourceManagerBlock() { Record.clear(); unsigned RecCode = Stream.ReadRecord(Code, Record, &BlobStart, &BlobLen); assert(RecCode == pch::SM_SLOC_BUFFER_BLOB && "Ill-formed PCH file"); - SourceMgr.createFileIDForMemBuffer( - llvm::MemoryBuffer::getMemBuffer(BlobStart, BlobStart + BlobLen - 1, - Name)); + llvm::MemoryBuffer *Buffer + = llvm::MemoryBuffer::getMemBuffer(BlobStart, + BlobStart + BlobLen - 1, + Name); + FileID BufferID = SourceMgr.createFileIDForMemBuffer(Buffer); + + if (strcmp(Name, "") == 0 + && CheckPredefinesBuffer(BlobStart, BlobLen - 1, BufferID)) + return IgnorePCH; break; } @@ -329,9 +417,16 @@ PCHReader::PCHReadResult PCHReader::ReadPCHBlock() { break; case pch::SOURCE_MANAGER_BLOCK_ID: - if (ReadSourceManagerBlock()) { + switch (ReadSourceManagerBlock()) { + case Success: + break; + + case Failure: Error("Malformed source manager block"); return Failure; + + case IgnorePCH: + return IgnorePCH; } break; @@ -400,15 +495,17 @@ PCHReader::PCHReadResult PCHReader::ReadPCHBlock() { PCHReader::~PCHReader() { } -bool PCHReader::ReadPCH(const std::string &FileName) { +PCHReader::PCHReadResult PCHReader::ReadPCH(const std::string &FileName) { // Set the PCH file name. this->FileName = FileName; // Open the PCH file. std::string ErrStr; Buffer.reset(llvm::MemoryBuffer::getFile(FileName.c_str(), &ErrStr)); - if (!Buffer) - return Error(ErrStr.c_str()); + if (!Buffer) { + Error(ErrStr.c_str()); + return IgnorePCH; + } // Initialize the stream Stream.init((const unsigned char *)Buffer->getBufferStart(), @@ -418,24 +515,30 @@ bool PCHReader::ReadPCH(const std::string &FileName) { if (Stream.Read(8) != 'C' || Stream.Read(8) != 'P' || Stream.Read(8) != 'C' || - Stream.Read(8) != 'H') - return Error("Not a PCH file"); + Stream.Read(8) != 'H') { + Error("Not a PCH file"); + return IgnorePCH; + } // We expect a number of well-defined blocks, though we don't necessarily // need to understand them all. while (!Stream.AtEndOfStream()) { unsigned Code = Stream.ReadCode(); - if (Code != llvm::bitc::ENTER_SUBBLOCK) - return Error("Invalid record at top-level"); + if (Code != llvm::bitc::ENTER_SUBBLOCK) { + Error("Invalid record at top-level"); + return Failure; + } unsigned BlockID = Stream.ReadSubBlockID(); // We only know the PCH subblock ID. switch (BlockID) { case llvm::bitc::BLOCKINFO_BLOCK_ID: - if (Stream.ReadBlockInfoBlock()) - return Error("Malformed BlockInfoBlock"); + if (Stream.ReadBlockInfoBlock()) { + Error("Malformed BlockInfoBlock"); + return Failure; + } break; case pch::PCH_BLOCK_ID: switch (ReadPCHBlock()) { @@ -443,18 +546,20 @@ bool PCHReader::ReadPCH(const std::string &FileName) { break; case Failure: - return true; + return Failure; case IgnorePCH: // FIXME: We could consider reading through to the end of this // PCH block, skipping subblocks, to see if there are other // PCH blocks elsewhere. - return false; + return IgnorePCH; } break; default: - if (Stream.SkipBlock()) - return Error("Malformed block record"); + if (Stream.SkipBlock()) { + Error("Malformed block record"); + return Failure; + } break; } } @@ -462,13 +567,7 @@ bool PCHReader::ReadPCH(const std::string &FileName) { // Load the translation unit declaration ReadDeclRecord(DeclOffsets[0], 0); - // If everything looks like it will be ok, then the PCH file load succeeded. - // Since the PCH file contains everything that is in the preprocessor's - // predefines buffer (and we validated that they are the same) clear out the - // predefines buffer so that it doesn't get processed again. - PP.setPredefines(""); - - return false; + return Success; } /// \brief Parse the record that corresponds to a LangOptions data @@ -875,7 +974,11 @@ PCHReader::ReadDeclarationName(const RecordData &Record, unsigned &Idx) { } DiagnosticBuilder PCHReader::Diag(unsigned DiagID) { - return PP.getDiagnostics().Report(FullSourceLoc(SourceLocation(), + return Diag(SourceLocation(), DiagID); +} + +DiagnosticBuilder PCHReader::Diag(SourceLocation Loc, unsigned DiagID) { + return PP.getDiagnostics().Report(FullSourceLoc(Loc, Context.getSourceManager()), DiagID); } diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index 09080d640b..4963ea1160 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -872,7 +872,7 @@ void PCHWriter::AddTypeRef(QualType T, RecordData &Record) { } if (const BuiltinType *BT = dyn_cast(T.getTypePtr())) { - pch::TypeID ID; + pch::TypeID ID = 0; switch (BT->getKind()) { case BuiltinType::Void: ID = pch::PREDEF_TYPE_VOID_ID; break; case BuiltinType::Bool: ID = pch::PREDEF_TYPE_BOOL_ID; break; diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 5735d01716..097b4542b6 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -45,10 +45,6 @@ using namespace clang; PreprocessorFactory::~PreprocessorFactory() {} -bool PreprocessorFactory::FinishInitialization(Preprocessor *PP, bool UsesPCH) { - return false; -} - Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts, TargetInfo &target, SourceManager &SM, HeaderSearch &Headers, diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 4f87b48eff..474c1e490e 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -524,22 +524,6 @@ bool Sema::MergeTypeDefDecl(TypedefDecl *New, Decl *OldD) { return false; } - // __builtin_va_list gets redeclared in the built-in definitions - // buffer when using PCH. Don't complain about such redefinitions. - // - // FIXME: The problem here is that the __builtin_va_list declaration - // comes in as target-specific text in the predefines buffer, both - // in the generation of the PCH file and in the source file. Thus, - // we end up with two typedefs for the same type, which is an error - // in C. Our hackish solution is to allow redundant typedefs *to the - // same type* if the types are defined in the predefined buffer. We - // would like to eliminate this ugliness, perhaps by making - // __builtin_va_list a real, Sema-supplied declaration rather than - // putting its text into the predefines buffer. - if (Context.getExternalSource() && - strcmp(SourceMgr.getBufferName(New->getLocation()), "") == 0) - return false; - Diag(New->getLocation(), diag::err_redefinition) << New->getDeclName(); Diag(Old->getLocation(), diag::note_previous_definition); return true; diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index dfdc1953fe..9ff59b55fd 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -1437,50 +1437,46 @@ static void InitializePredefinedMacros(const TargetInfo &TI, TI.getTargetDefines(LangOpts, Buf); } -/// InitializePreprocessor - Initialize the preprocessor getting it and the -/// environment ready to process a single file. This returns true on error. -/// -static bool InitializePreprocessor(Preprocessor &PP, - bool InitializeSourceMgr, - const std::string &InFile, bool UsesPCH) { - FileManager &FileMgr = PP.getFileManager(); - +static bool InitializeSourceManager(Preprocessor &PP, + const std::string &InFile) { // Figure out where to get and map in the main file. SourceManager &SourceMgr = PP.getSourceManager(); + FileManager &FileMgr = PP.getFileManager(); + + if (InFile != "-") { + const FileEntry *File = FileMgr.getFile(InFile); + if (File) SourceMgr.createMainFileID(File, SourceLocation()); + if (SourceMgr.getMainFileID().isInvalid()) { + PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading) + << InFile.c_str(); + return true; + } + } else { + llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getSTDIN(); - if (InitializeSourceMgr) { - if (InFile != "-") { - const FileEntry *File = FileMgr.getFile(InFile); - if (File) SourceMgr.createMainFileID(File, SourceLocation()); - if (SourceMgr.getMainFileID().isInvalid()) { - PP.getDiagnostics().Report(FullSourceLoc(), diag::err_fe_error_reading) - << InFile.c_str(); - return true; - } - } else { - llvm::MemoryBuffer *SB = llvm::MemoryBuffer::getSTDIN(); - - // If stdin was empty, SB is null. Cons up an empty memory - // buffer now. - if (!SB) { - const char *EmptyStr = ""; - SB = llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, ""); - } + // If stdin was empty, SB is null. Cons up an empty memory + // buffer now. + if (!SB) { + const char *EmptyStr = ""; + SB = llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, ""); + } - SourceMgr.createMainFileIDForMemBuffer(SB); - if (SourceMgr.getMainFileID().isInvalid()) { - PP.getDiagnostics().Report(FullSourceLoc(), - diag::err_fe_error_reading_stdin); - return true; - } + SourceMgr.createMainFileIDForMemBuffer(SB); + if (SourceMgr.getMainFileID().isInvalid()) { + PP.getDiagnostics().Report(FullSourceLoc(), + diag::err_fe_error_reading_stdin); + return true; } } - // If the file is using PCH, then the PCH will include all the predefines, no - // need to install them now. - if (UsesPCH) - return false; - + return false; +} + +/// InitializePreprocessor - Initialize the preprocessor getting it and the +/// environment ready to process a single file. This returns true on error. +/// +static bool InitializePreprocessor(Preprocessor &PP, + const std::string &InFile) { std::vector PredefineBuffer; // Install things like __POWERPC__, __GNUC__, etc into the macro table. @@ -1721,7 +1717,6 @@ class VISIBILITY_HIDDEN DriverPreprocessorFactory : public PreprocessorFactory { TargetInfo &Target; SourceManager &SourceMgr; HeaderSearch &HeaderInfo; - bool InitializeSourceMgr; public: DriverPreprocessorFactory(const std::string &infile, @@ -1729,7 +1724,7 @@ public: TargetInfo &target, SourceManager &SM, HeaderSearch &Headers) : InFile(infile), Diags(diags), LangInfo(opts), Target(target), - SourceMgr(SM), HeaderInfo(Headers), InitializeSourceMgr(true) {} + SourceMgr(SM), HeaderInfo(Headers) {} virtual ~DriverPreprocessorFactory() {} @@ -1766,26 +1761,21 @@ public: PTHMgr->setPreprocessor(PP.get()); PP->setPTHManager(PTHMgr.take()); } - - return PP.take(); - } - virtual bool FinishInitialization(Preprocessor *PP, bool UsesPCH) { - if (InitializePreprocessor(*PP, InitializeSourceMgr, InFile, UsesPCH)) - return true; + if (InitializePreprocessor(*PP, InFile)) + return 0; /// FIXME: PP can only handle one callback if (ProgAction != PrintPreprocessedInput) { std::string ErrStr; - bool DFG = CreateDependencyFileGen(PP, ErrStr); + bool DFG = CreateDependencyFileGen(PP.get(), ErrStr); if (!DFG && !ErrStr.empty()) { fprintf(stderr, "%s", ErrStr.c_str()); - return true; + return 0; } } - InitializeSourceMgr = false; - return false; + return PP.take(); } }; } @@ -2089,19 +2079,37 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, if (!ImplicitIncludePCH.empty()) { // The user has asked us to include a precompiled header. Load // the precompiled header into the AST context. - llvm::OwningPtr - Reader(new clang::PCHReader(PP, *ContextOwner.get())); - if (Reader->ReadPCH(ImplicitIncludePCH)) + llvm::OwningPtr Reader(new PCHReader(PP, *ContextOwner.get())); + switch (Reader->ReadPCH(ImplicitIncludePCH)) { + case PCHReader::Success: { + // Attach the PCH reader to the AST context as an external AST + // source, so that declarations will be deserialized from the + // PCH file as needed. + llvm::OwningPtr Source(Reader.take()); + ContextOwner->setExternalSource(Source); + + // Clear out the predefines buffer, because all of the + // predefines are already in the PCH file. + PP.setPredefines(""); + break; + } + + case PCHReader::Failure: + // Unrecoverable failure: don't even try to process the input + // file. return; - llvm::OwningPtr Source(Reader.take()); - ContextOwner->setExternalSource(Source); + case PCHReader::IgnorePCH: + // No suitable PCH file could be found. Just ignore the + // -include-pch option entirely. + break; + } // Finish preprocessor initialization. We do this now (rather // than earlier) because this initialization creates new source // location entries in the source manager, which must come after // the source location entries for the PCH file. - if (PPF.FinishInitialization(&PP, true /*uses PCH*/)) + if (InitializeSourceManager(PP, InFile)) return; } @@ -2311,8 +2319,8 @@ int main(int argc, char **argv) { if (!PP) continue; - if (ImplicitIncludePCH.empty() - && PPFactory.FinishInitialization(PP.get(), false)) + if (ImplicitIncludePCH.empty() && + InitializeSourceManager(*PP.get(), InFile)) continue; // Create the HTMLDiagnosticsClient if we are using one. Otherwise,