diff options
-rw-r--r-- | include/clang/Basic/DiagnosticFrontendKinds.td | 5 | ||||
-rw-r--r-- | include/clang/Frontend/PCHReader.h | 16 | ||||
-rw-r--r-- | include/clang/Lex/Preprocessor.h | 1 | ||||
-rw-r--r-- | lib/Frontend/PCHReader.cpp | 169 | ||||
-rw-r--r-- | lib/Frontend/PCHWriter.cpp | 2 | ||||
-rw-r--r-- | lib/Lex/Preprocessor.cpp | 4 | ||||
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 16 | ||||
-rw-r--r-- | tools/clang-cc/clang-cc.cpp | 122 |
8 files changed, 219 insertions, 116 deletions
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<uint64_t> &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, + "<built-in>"); + BufferID = SourceMgr.createFileIDForMemBuffer(Buffer); + } + + unsigned MinLen = std::min(PredefLen, PCHPredefLen); + std::pair<const char *, const char *> 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, "<built-in>") == 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<BuiltinType>(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()), "<built-in>") == 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, "<stdin>"); - } + // 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, "<stdin>"); + } - 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<char> 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<PCHReader> - Reader(new clang::PCHReader(PP, *ContextOwner.get())); - if (Reader->ReadPCH(ImplicitIncludePCH)) + llvm::OwningPtr<PCHReader> 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<ExternalASTSource> 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<ExternalASTSource> 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, |