]> granicus.if.org Git - clang/commitdiff
Compare the predefines buffer in the PCH file with the predefines
authorDouglas Gregor <dgregor@apple.com>
Fri, 10 Apr 2009 23:10:45 +0000 (23:10 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 10 Apr 2009 23:10:45 +0000 (23:10 +0000)
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

include/clang/Basic/DiagnosticFrontendKinds.td
include/clang/Frontend/PCHReader.h
include/clang/Lex/Preprocessor.h
lib/Frontend/PCHReader.cpp
lib/Frontend/PCHWriter.cpp
lib/Lex/Preprocessor.cpp
lib/Sema/SemaDecl.cpp
tools/clang-cc/clang-cc.cpp

index 9db74e251802f26231396cc72d56415f86502e58..55d1dfacb88a63f3afcb5276618d9a8ef3b2f5df 100644 (file)
@@ -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">;
 }
index 0c14df070e086d0415bc370992fe5f98d584a7dc..3efb3a55d68add7194a9239aba3026837a7720d7 100644 (file)
@@ -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);
index 656ebed89951f8462f32cf2cdb233dddc5282936..47f40b68f6cd45c1d5ff8522d192dac7efb83cbb 100644 (file)
@@ -788,7 +788,6 @@ class PreprocessorFactory {
 public:
   virtual ~PreprocessorFactory();
   virtual Preprocessor* CreatePreprocessor() = 0;  
-  virtual bool FinishInitialization(Preprocessor *PP, bool usesPCH);
 };
   
 }  // end namespace clang
index eaba610137dd23ff63674830618fb90c9acc8c74..345e673b47193b981498bc34cb7b5d4bf2b47dca 100644 (file)
@@ -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);
 }
index 09080d640b5ea9361866057950cc5d1347249a15..4963ea1160862ef8eb33a54418bc5463c53cfb96 100644 (file)
@@ -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;
index 5735d01716489339aea3ec3f04a00f776b4443a3..097b4542b6e46f47a8a7750f31cee806ac5301d5 100644 (file)
@@ -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,
index 4f87b48eff0cd062078d3a5a0967d8dd4afa9f34..474c1e490e43c3648e8d603d279abb1b51ce9393 100644 (file)
@@ -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;
index dfdc1953fea55676a83e9e4c5f08241819ad026a..9ff59b55fd1baab9e66cbde7b351efe0b5c50562 100644 (file)
@@ -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,