]> granicus.if.org Git - clang/commitdiff
Put the mechanism in place to track modifications in an AST entity that were committe...
authorArgyrios Kyrtzidis <akyrtzi@gmail.com>
Sun, 24 Oct 2010 17:26:36 +0000 (17:26 +0000)
committerArgyrios Kyrtzidis <akyrtzi@gmail.com>
Sun, 24 Oct 2010 17:26:36 +0000 (17:26 +0000)
its initial creation/deserialization and store the changes in a chained PCH.

The idea is that the AST entities call methods on the ASTMutationListener to give notifications
of changes; the PCHWriter implements the ASTMutationListener interface and stores the incremental changes
of the updated entity. WIP

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@117235 91177308-0d34-0410-b5e6-96231b3b80d8

16 files changed:
include/clang/AST/ASTConsumer.h
include/clang/AST/ASTContext.h
include/clang/AST/ASTMutationListener.h [new file with mode: 0644]
include/clang/AST/DeclBase.h
include/clang/Serialization/ASTBitCodes.h
include/clang/Serialization/ASTDeserializationListener.h
include/clang/Serialization/ASTReader.h
include/clang/Serialization/ASTWriter.h
lib/AST/ASTContext.cpp
lib/AST/DeclBase.cpp
lib/AST/DeclCXX.cpp
lib/Frontend/FrontendAction.cpp
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriter.cpp
lib/Serialization/GeneratePCH.cpp

index 84833c099f97658db60becdf69f922b29020d5bd..08ee4ef40de0649f6e527625c12f492b9ce15275 100644 (file)
@@ -19,6 +19,7 @@ namespace clang {
   class CXXRecordDecl;
   class DeclGroupRef;
   class HandleTagDeclDefinition;
+  class ASTMutationListener;
   class ASTDeserializationListener; // layering violation because void* is ugly
   class SemaConsumer; // layering violation required for safe SemaConsumer
   class TagDecl;
@@ -86,10 +87,13 @@ public:
   /// it was actually used.
   virtual void HandleVTable(CXXRecordDecl *RD, bool DefinitionRequired) {}
 
+  /// \brief If the consumer is interested in entities getting modified after
+  /// their initial creation, it should return a pointer to
+  /// a GetASTMutationListener here.
+  virtual ASTMutationListener *GetASTMutationListener() { return 0; }
+
   /// \brief If the consumer is interested in entities being deserialized from
   /// AST files, it should return a pointer to a ASTDeserializationListener here
-  ///
-  /// The return type is void* because ASTDS lives in Frontend.
   virtual ASTDeserializationListener *GetASTDeserializationListener() { return 0; }
 
   /// PrintStats - If desired, print any statistics.
index 0a960ab34c07023e5dc5b515d4579b378bfaa2e1..33f522f91b1f670cc2e146a36aac6ed93e3e438a 100644 (file)
@@ -45,6 +45,7 @@ namespace clang {
   class Diagnostic;
   class Expr;
   class ExternalASTSource;
+  class ASTMutationListener;
   class IdentifierTable;
   class SelectorTable;
   class SourceManager;
@@ -297,6 +298,7 @@ public:
   Builtin::Context &BuiltinInfo;
   DeclarationNameTable DeclarationNames;
   llvm::OwningPtr<ExternalASTSource> ExternalSource;
+  ASTMutationListener *Listener;
   clang::PrintingPolicy PrintingPolicy;
 
   // Typedefs which may be provided defining the structure of Objective-C
@@ -412,6 +414,19 @@ public:
   /// with this AST context, if any.
   ExternalASTSource *getExternalSource() const { return ExternalSource.get(); }
 
+  /// \brief Attach an AST mutation listener to the AST context.
+  ///
+  /// The AST mutation listener provides the ability to track modifications to
+  /// the abstract syntax tree entities committed after they were initially
+  /// created.
+  void setASTMutationListener(ASTMutationListener *Listener) {
+    this->Listener = Listener;
+  }
+
+  /// \brief Retrieve a pointer to the AST mutation listener associated
+  /// with this AST context, if any.
+  ASTMutationListener *getASTMutationListener() const { return Listener; }
+
   void PrintStats() const;
   const std::vector<Type*>& getTypes() const { return Types; }
 
diff --git a/include/clang/AST/ASTMutationListener.h b/include/clang/AST/ASTMutationListener.h
new file mode 100644 (file)
index 0000000..ea09b0c
--- /dev/null
@@ -0,0 +1,30 @@
+//===--- ASTMutationListener.h - AST Mutation Interface --------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the ASTMutationListener interface.
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_CLANG_AST_ASTMUTATIONLISTENER_H
+#define LLVM_CLANG_AST_ASTMUTATIONLISTENER_H
+
+namespace clang {
+  class CXXRecordDecl;
+  class CXXMethodDecl;
+
+/// \brief An abstract interface that should be implemented by listeners
+/// that want to be notified when an AST entity gets modified after its
+/// initial creation.
+class ASTMutationListener {
+public:
+  virtual ~ASTMutationListener();
+};
+
+} // end namespace clang
+
+#endif
index 83100178d58a8201a7186e8594f1dea3078805dd..073efa69026a929f067901ca0b30a6bcb5df13b9 100644 (file)
@@ -43,6 +43,7 @@ class DeclarationName;
 class CompoundStmt;
 class StoredDeclsMap;
 class DependentDiagnostic;
+class ASTMutationListener;
 }
 
 namespace llvm {
@@ -625,6 +626,8 @@ public:
 private:
   const Attr *getAttrsImpl() const;
 
+protected:
+  ASTMutationListener *getASTMutationListener() const;
 };
 
 /// PrettyStackTraceDecl - If a crash occurs, indicate that it happened when
index da27cde29f8106741c5c638be2976260bde6674d..7370db1fc49ad174f527f2464bb983dd6de71777 100644 (file)
@@ -141,7 +141,10 @@ namespace clang {
 
       /// \brief The block containing the definitions of all of the
       /// types and decls used within the AST file.
-      DECLTYPES_BLOCK_ID
+      DECLTYPES_BLOCK_ID,
+
+      /// \brief The block containing DECL_UPDATES records.
+      DECL_UPDATES_BLOCK_ID
     };
 
     /// \brief Record types that occur within the AST block itself.
@@ -326,7 +329,15 @@ namespace clang {
 
       /// \brief Record code for template specializations introduced after
       /// serializations of the original template decl.
-      ADDITIONAL_TEMPLATE_SPECIALIZATIONS = 35
+      ADDITIONAL_TEMPLATE_SPECIALIZATIONS = 35,
+
+      /// \brief Record for offsets of DECL_UPDATES records for declarations
+      /// that were modified after being deserialized and need updates.
+      DECL_UPDATE_OFFSETS = 36,
+
+      /// \brief Record of updates for a declaration that was modified after
+      /// being deserialized.
+      DECL_UPDATES = 37
     };
 
     /// \brief Record types used within a source manager block.
index 00860cd39bda107f0a8082818e19e6ea50a2367b..f8cdebe5a91350eb47ea29bb415eea7124e18d2d 100644 (file)
@@ -29,8 +29,9 @@ protected:
   virtual ~ASTDeserializationListener();
 
 public:
-  /// \brief Tell the listener about the reader.
-  virtual void SetReader(ASTReader *Reader) { }
+
+  /// \brief The ASTReader was initialized.
+  virtual void ReaderInitialized(ASTReader *Reader) { }
 
   /// \brief An identifier was deserialized from the AST file.
   virtual void IdentifierRead(serialization::IdentID ID,
index b5766b14ebcb821a6fabaf00d9aea57c0dbf0947..324e093e730014f817969b2d48e0ad32cdb7b9b3 100644 (file)
@@ -401,6 +401,14 @@ private:
   /// = I + 1 has already been loaded.
   std::vector<Decl *> DeclsLoaded;
 
+  typedef std::pair<PerFileData *, uint64_t> FileOffset;
+  typedef llvm::SmallVector<FileOffset, 2> FileOffsetsTy;
+  typedef llvm::DenseMap<serialization::DeclID, FileOffsetsTy>
+      DeclUpdateOffsetsMap;
+  /// \brief Declarations that have modifications residing in a later file
+  /// in the chain.
+  DeclUpdateOffsetsMap DeclUpdateOffsets;
+
   typedef llvm::DenseMap<serialization::DeclID,
                          std::pair<PerFileData *, uint64_t> >
       DeclReplacementMap;
index b2eccf393ee74318898f651e7f7bbffaa6e1419f..f1a66fcbc7b8d2c67794bc16651cd54d81ec7145 100644 (file)
@@ -17,6 +17,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/TemplateBase.h"
+#include "clang/AST/ASTMutationListener.h"
 #include "clang/Serialization/ASTBitCodes.h"
 #include "clang/Serialization/ASTDeserializationListener.h"
 #include "clang/Sema/SemaConsumer.h"
@@ -55,9 +56,11 @@ class TargetInfo;
 /// representation of a given abstract syntax tree and its supporting
 /// data structures. This bitstream can be de-serialized via an
 /// instance of the ASTReader class.
-class ASTWriter : public ASTDeserializationListener {
+class ASTWriter : public ASTDeserializationListener,
+                  public ASTMutationListener {
 public:
   typedef llvm::SmallVector<uint64_t, 64> RecordData;
+  typedef llvm::SmallVectorImpl<uint64_t> RecordDataImpl;
 
   friend class ASTDeclWriter;
 private:
@@ -187,6 +190,12 @@ private:
   /// to the corresponding offsets within the preprocessor block.
   std::vector<uint32_t> MacroDefinitionOffsets;
 
+  typedef llvm::SmallVector<uint64_t, 2> UpdateRecord;
+  typedef llvm::DenseMap<const Decl *, UpdateRecord> DeclUpdateMap;
+  /// \brief Mapping from declarations that came from a chained PCH to the
+  /// record containing modifications to them.
+  DeclUpdateMap DeclUpdates;
+
   typedef llvm::DenseMap<Decl *, Decl *> FirstLatestDeclMap;
   /// \brief Map of first declarations from a chained PCH that point to the
   /// most recent declarations in another PCH.
@@ -280,6 +289,7 @@ private:
   void WriteReferencedSelectorsPool(Sema &SemaRef);
   void WriteIdentifierTable(Preprocessor &PP);
   void WriteAttributes(const AttrVec &Attrs, RecordData &Record);
+  void WriteDeclChangeSetBlocks();
   void WriteDeclUpdateBlock();
   void WriteDeclContextVisibleUpdate(const DeclContext *DC);
   void WriteAdditionalTemplateSpecializations();
@@ -389,7 +399,7 @@ public:
                               RecordData &Record);
 
   /// \brief Emit a reference to a declaration.
-  void AddDeclRef(const Decl *D, RecordData &Record);
+  void AddDeclRef(const Decl *D, RecordDataImpl &Record);
 
   /// \brief Force a declaration to be emitted and get its ID.
   serialization::DeclID GetDeclRef(const Decl *D);
@@ -489,7 +499,7 @@ public:
   bool hasChain() const { return Chain; }
 
   // ASTDeserializationListener implementation
-  void SetReader(ASTReader *Reader);
+  void ReaderInitialized(ASTReader *Reader);
   void IdentifierRead(serialization::IdentID ID, IdentifierInfo *II);
   void TypeRead(serialization::TypeIdx Idx, QualType T);
   void DeclRead(serialization::DeclID ID, const Decl *D);
@@ -508,6 +518,7 @@ class PCHGenerator : public SemaConsumer {
   std::vector<unsigned char> Buffer;
   llvm::BitstreamWriter Stream;
   ASTWriter Writer;
+  bool Chaining;
 
 protected:
   ASTWriter &getWriter() { return Writer; }
@@ -518,6 +529,7 @@ public:
                const char *isysroot, llvm::raw_ostream *Out);
   virtual void InitializeSema(Sema &S) { SemaPtr = &S; }
   virtual void HandleTranslationUnit(ASTContext &Ctx);
+  virtual ASTMutationListener *GetASTMutationListener();
   virtual ASTDeserializationListener *GetASTDeserializationListener();
 };
 
index c7b014efb5c61674fbaa0ff3fd7ec3cd54ae106d..313c2ea96b6e688b65b7748938997dd7f50ec997 100644 (file)
@@ -20,6 +20,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/ExternalASTSource.h"
+#include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/RecordLayout.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
@@ -5200,6 +5201,8 @@ ExternalASTSource::~ExternalASTSource() { }
 
 void ExternalASTSource::PrintStats() { }
 
+ASTMutationListener::~ASTMutationListener() { }
+
 
 //===----------------------------------------------------------------------===//
 //                          Builtin Type Computation
index d60a06749d5a4abe42f0b2068908b43c065f87fd..483352f2d623bf696ed9b1d9f7717061c566b968 100644 (file)
@@ -210,6 +210,10 @@ ASTContext &Decl::getASTContext() const {
   return getTranslationUnitDecl()->getASTContext();
 }
 
+ASTMutationListener *Decl::getASTMutationListener() const {
+  return getASTContext().getASTMutationListener();
+}
+
 bool Decl::isUsed(bool CheckUsedAttr) const { 
   if (Used)
     return true;
index 8e1de4d027c5d88a4af985e6d9cbaee5a42dada5..f37151439f9fe9a3ed50bcc3b0bf79dad8249d27 100644 (file)
@@ -14,6 +14,7 @@
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/TypeLoc.h"
index 97188919f9a8066f009ca0eb106ea7fe92d2c7a3..26ae4b1f7f8ea1af901ffd032230276e17f5f725 100644 (file)
@@ -167,6 +167,8 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
 
     llvm::OwningPtr<ASTConsumer> Consumer(CreateASTConsumer(CI, Filename));
 
+    CI.getASTContext().setASTMutationListener(Consumer->GetASTMutationListener());
+
     /// Use PCH?
     if (!CI.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
       assert(hasPCHSupport() && "This action does not have PCH support!");
index 162fa26e2dfa60c47c69fc4db01e482a3e9968f7..38e786bff10b477dde91a1dbad2e9d043cf9dee4 100644 (file)
@@ -454,8 +454,6 @@ void PCHValidator::ReadCounter(unsigned Value) {
 void
 ASTReader::setDeserializationListener(ASTDeserializationListener *Listener) {
   DeserializationListener = Listener;
-  if (DeserializationListener)
-    DeserializationListener->SetReader(this);
 }
 
 
@@ -1716,6 +1714,13 @@ ASTReader::ReadASTBlock(PerFileData &F) {
         }
         break;
 
+      case DECL_UPDATES_BLOCK_ID:
+        if (Stream.SkipBlock()) {
+          Error("malformed block record in AST file");
+          return Failure;
+        }
+        break;
+
       case PREPROCESSOR_BLOCK_ID:
         F.MacroCursor = Stream;
         if (PP)
@@ -2043,6 +2048,17 @@ ASTReader::ReadASTBlock(PerFileData &F) {
       F.LocalNumMacroDefinitions = Record[1];
       break;
 
+    case DECL_UPDATE_OFFSETS: {
+      if (Record.size() % 2 != 0) {
+        Error("invalid DECL_UPDATE_OFFSETS block in AST file");
+        return Failure;
+      }
+      for (unsigned I = 0, N = Record.size(); I != N; I += 2)
+        DeclUpdateOffsets[static_cast<DeclID>(Record[I])]
+            .push_back(std::make_pair(&F, Record[I+1]));
+      break;
+    }
+
     case DECL_REPLACEMENTS: {
       if (Record.size() % 2 != 0) {
         Error("invalid DECL_REPLACEMENTS block in AST file");
@@ -2164,6 +2180,9 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName,
   if (Context)
     InitializeContext(*Context);
 
+  if (DeserializationListener)
+    DeserializationListener->ReaderInitialized(this);
+
   return Success;
 }
 
index 750e7236ef414293eb1046dd0e5b4d2871faf179..acc4dc144d1dfc81ce9de4da5f8949718994fd1b 100644 (file)
@@ -12,6 +12,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "ASTCommon.h"
 #include "clang/Serialization/ASTReader.h"
 #include "clang/AST/ASTConsumer.h"
 #include "clang/AST/ASTContext.h"
@@ -71,6 +72,8 @@ namespace clang {
 
     void Visit(Decl *D);
 
+    void UpdateDecl(Decl *D, const RecordData &Record);
+
     void VisitDecl(Decl *D);
     void VisitTranslationUnitDecl(TranslationUnitDecl *TU);
     void VisitNamedDecl(NamedDecl *ND);
@@ -1537,6 +1540,28 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) {
   }
   assert(Idx == Record.size());
 
+  // The declaration may have been modified by files later in the chain.
+  // If this is the case, read the record containing the updates from each file
+  // and pass it to ASTDeclReader to make the modifications.
+  DeclUpdateOffsetsMap::iterator UpdI = DeclUpdateOffsets.find(ID);
+  if (UpdI != DeclUpdateOffsets.end()) {
+    FileOffsetsTy &UpdateOffsets = UpdI->second;
+    for (FileOffsetsTy::iterator
+           I = UpdateOffsets.begin(), E = UpdateOffsets.end(); I != E; ++I) {
+      PerFileData *F = I->first;
+      uint64_t Offset = I->second;
+      llvm::BitstreamCursor &Cursor = F->DeclsCursor;
+      SavedStreamPosition SavedPosition(Cursor);
+      Cursor.JumpToBit(Offset);
+      RecordData Record;
+      unsigned Code = Cursor.ReadCode();
+      unsigned RecCode = Cursor.ReadRecord(Code, Record);
+      (void)RecCode;
+      assert(RecCode == DECL_UPDATES && "Expected DECL_UPDATES record!");
+      Reader.UpdateDecl(D, Record);
+    }
+  }
+
   // If we have deserialized a declaration that has a definition the
   // AST consumer might need to know about, queue it.
   // We don't pass it to the consumer immediately because we may be in recursive
@@ -1546,3 +1571,7 @@ Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) {
 
   return D;
 }
+
+void ASTDeclReader::UpdateDecl(Decl *D, const RecordData &Record) {
+  // No update is tracked yet.
+}
index 4d0ec990749881a788e38cc3496a1175283875d0..e258fcc2a29694f7d05fbfcff051464020d0fc41 100644 (file)
@@ -2474,17 +2474,6 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
                               const char *isysroot) {
   using namespace llvm;
 
-  FirstDeclID += Chain->getTotalNumDecls();
-  FirstTypeID += Chain->getTotalNumTypes();
-  FirstIdentID += Chain->getTotalNumIdentifiers();
-  FirstSelectorID += Chain->getTotalNumSelectors();
-  FirstMacroID += Chain->getTotalNumMacroDefinitions();
-  NextDeclID = FirstDeclID;
-  NextTypeID = FirstTypeID;
-  NextIdentID = FirstIdentID;
-  NextSelectorID = FirstSelectorID;
-  NextMacroID = FirstMacroID;
-
   ASTContext &Context = SemaRef.Context;
   Preprocessor &PP = SemaRef.PP;
 
@@ -2707,6 +2696,8 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
   if (!AdditionalTemplateSpecializations.empty())
     WriteAdditionalTemplateSpecializations();
 
+  WriteDeclChangeSetBlocks();
+
   Record.clear();
   Record.push_back(NumStatements);
   Record.push_back(NumMacros);
@@ -2717,6 +2708,27 @@ void ASTWriter::WriteASTChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
   Stream.ExitBlock();
 }
 
+void ASTWriter::WriteDeclChangeSetBlocks() {
+  if (DeclUpdates.empty())
+    return;
+
+  RecordData OffsetsRecord;
+  Stream.EnterSubblock(DECL_UPDATES_BLOCK_ID, 3);
+  for (DeclUpdateMap::iterator
+         I = DeclUpdates.begin(), E = DeclUpdates.end(); I != E; ++I) {
+    const Decl *D = I->first;
+    UpdateRecord &URec = I->second;
+
+    uint64_t Offset = Stream.GetCurrentBitNo();
+    Stream.EmitRecord(DECL_UPDATES, URec);
+
+    OffsetsRecord.push_back(GetDeclRef(D));
+    OffsetsRecord.push_back(Offset);
+  }
+  Stream.ExitBlock();
+  Stream.EmitRecord(DECL_UPDATE_OFFSETS, OffsetsRecord);
+}
+
 void ASTWriter::WriteDeclUpdateBlock() {
   if (ReplacedDecls.empty())
     return;
@@ -2891,7 +2903,7 @@ TypeIdx ASTWriter::getTypeIdx(QualType T) const {
   return I->second;
 }
 
-void ASTWriter::AddDeclRef(const Decl *D, RecordData &Record) {
+void ASTWriter::AddDeclRef(const Decl *D, RecordDataImpl &Record) {
   Record.push_back(GetDeclRef(D));
 }
 
@@ -3190,8 +3202,9 @@ void ASTWriter::AddCXXBaseOrMemberInitializers(
   }
 }
 
-void ASTWriter::SetReader(ASTReader *Reader) {
+void ASTWriter::ReaderInitialized(ASTReader *Reader) {
   assert(Reader && "Cannot remove chain");
+  assert(!Chain && "Cannot replace chain");
   assert(FirstDeclID == NextDeclID &&
          FirstTypeID == NextTypeID &&
          FirstIdentID == NextIdentID &&
@@ -3199,6 +3212,17 @@ void ASTWriter::SetReader(ASTReader *Reader) {
          FirstMacroID == NextMacroID &&
          "Setting chain after writing has started.");
   Chain = Reader;
+
+  FirstDeclID += Chain->getTotalNumDecls();
+  FirstTypeID += Chain->getTotalNumTypes();
+  FirstIdentID += Chain->getTotalNumIdentifiers();
+  FirstSelectorID += Chain->getTotalNumSelectors();
+  FirstMacroID += Chain->getTotalNumMacroDefinitions();
+  NextDeclID = FirstDeclID;
+  NextTypeID = FirstTypeID;
+  NextIdentID = FirstIdentID;
+  NextSelectorID = FirstSelectorID;
+  NextMacroID = FirstMacroID;
 }
 
 void ASTWriter::IdentifierRead(IdentID ID, IdentifierInfo *II) {
index 5329b6cbd4d0f9d797a8177bf47a36ab34ef4cbf..0d8ec736b6ad184b32e30b9f11c5b82289060e95 100644 (file)
@@ -30,7 +30,7 @@ PCHGenerator::PCHGenerator(const Preprocessor &PP,
                            const char *isysroot,
                            llvm::raw_ostream *OS)
   : PP(PP), isysroot(isysroot), Out(OS), SemaPtr(0),
-    StatCalls(0), Stream(Buffer), Writer(Stream) {
+    StatCalls(0), Stream(Buffer), Writer(Stream), Chaining(Chaining) {
 
   // Install a stat() listener to keep track of all of the stat()
   // calls.
@@ -59,6 +59,12 @@ void PCHGenerator::HandleTranslationUnit(ASTContext &Ctx) {
   Buffer.clear();
 }
 
+ASTMutationListener *PCHGenerator::GetASTMutationListener() {
+  if (Chaining)
+    return &Writer;
+  return 0;
+}
+
 ASTDeserializationListener *PCHGenerator::GetASTDeserializationListener() {
   return &Writer;
 }