"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">;
}
/// 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;
/// 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);
~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.
/// \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);
public:
virtual ~PreprocessorFactory();
virtual Preprocessor* CreatePreprocessor() = 0;
- virtual bool FinishInitialization(Preprocessor *PP, bool usesPCH);
};
} // end namespace clang
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;
}
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;
}
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;
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(),
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()) {
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;
}
}
// 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
}
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);
}
}
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;
PreprocessorFactory::~PreprocessorFactory() {}
-bool PreprocessorFactory::FinishInitialization(Preprocessor *PP, bool UsesPCH) {
- return false;
-}
-
Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts,
TargetInfo &target, SourceManager &SM,
HeaderSearch &Headers,
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;
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.
TargetInfo &Target;
SourceManager &SourceMgr;
HeaderSearch &HeaderInfo;
- bool InitializeSourceMgr;
public:
DriverPreprocessorFactory(const std::string &infile,
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() {}
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();
}
};
}
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;
}
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,