Instead of modifying the ObjC AST to not modify existing declarations, teach chained PCH to overwrite declarations from earlier PCH files in dependent ones. Tell Sema to note when it changes AST nodes so that they have to be reserialized. Finally, the ObjCProtocolDecls created in forward decls, like the ObjCInterfaceDecls in @class forward decls, are not lexically part of the decl context; only the definition is.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110989 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp
index 16afabb..e55605b 100644
--- a/lib/Frontend/PCHReader.cpp
+++ b/lib/Frontend/PCHReader.cpp
@@ -1774,6 +1774,17 @@
       F.NumPreallocatedPreprocessingEntities = Record[0];
       F.LocalNumMacroDefinitions = Record[1];
       break;
+
+    case pch::DECL_REPLACEMENTS: {
+      if (Record.size() % 2 != 0) {
+        Error("invalid DECL_REPLACEMENTS block in PCH file");
+        return Failure;
+      }
+      for (unsigned I = 0, N = Record.size(); I != N; I += 2)
+        ReplacedDecls[static_cast<pch::DeclID>(Record[I])] =
+            std::make_pair(&F, Record[I+1]);
+      break;
+    }
     }
     First = false;
   }
diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp
index 212eaa1..aa5ce7a 100644
--- a/lib/Frontend/PCHReaderDecl.cpp
+++ b/lib/Frontend/PCHReaderDecl.cpp
@@ -1307,7 +1307,13 @@
 }
 
 /// \brief Get the correct cursor and offset for loading a type.
-PCHReader::RecordLocation PCHReader::DeclCursorForIndex(unsigned Index) {
+PCHReader::RecordLocation
+PCHReader::DeclCursorForIndex(unsigned Index, pch::DeclID ID) {
+  // See if there's an override.
+  DeclReplacementMap::iterator It = ReplacedDecls.find(ID);
+  if (It != ReplacedDecls.end())
+    return RecordLocation(&It->second.first->DeclsCursor, It->second.second);
+
   PerFileData *F = 0;
   for (unsigned I = 0, N = Chain.size(); I != N; ++I) {
     F = Chain[N - I - 1];
@@ -1321,7 +1327,7 @@
 
 /// \brief Read the declaration at the given offset from the PCH file.
 Decl *PCHReader::ReadDeclRecord(unsigned Index, pch::DeclID ID) {
-  RecordLocation Loc = DeclCursorForIndex(Index);
+  RecordLocation Loc = DeclCursorForIndex(Index, ID);
   llvm::BitstreamCursor &DeclsCursor = *Loc.first;
   // Keep track of where we are in the stream, then jump back there
   // after reading this declaration.
diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp
index d7da4fc..a86bdb9 100644
--- a/lib/Frontend/PCHWriter.cpp
+++ b/lib/Frontend/PCHWriter.cpp
@@ -2414,6 +2414,8 @@
        I != E; ++I) {
     if ((*I)->getPCHLevel() == 0)
       NewGlobalDecls.push_back(GetDeclRef(*I));
+    else if ((*I)->isChangedSinceDeserialization())
+      (void)GetDeclRef(*I); // Make sure it's written, but don't record it.
   }
   // We also need to write a lexical updates block for the TU.
   llvm::BitCodeAbbrev *Abv = new llvm::BitCodeAbbrev();
@@ -2596,10 +2598,24 @@
   Record.push_back(NumMacros);
   Record.push_back(NumLexicalDeclContexts);
   Record.push_back(NumVisibleDeclContexts);
+  WriteDeclUpdateBlock();
   Stream.EmitRecord(pch::STATISTICS, Record);
   Stream.ExitBlock();
 }
 
+void PCHWriter::WriteDeclUpdateBlock() {
+  if (ReplacedDecls.empty())
+    return;
+
+  RecordData Record;
+  for (llvm::SmallVector<std::pair<pch::DeclID, uint64_t>, 16>::iterator
+           I = ReplacedDecls.begin(), E = ReplacedDecls.end(); I != E; ++I) {
+    Record.push_back(I->first);
+    Record.push_back(I->second);
+  }
+  Stream.EmitRecord(pch::DECL_REPLACEMENTS, Record);
+}
+
 void PCHWriter::AddSourceLocation(SourceLocation Loc, RecordData &Record) {
   Record.push_back(Loc.getRawEncoding());
 }
@@ -2817,6 +2833,12 @@
     // enqueue it in the list of declarations to emit.
     ID = NextDeclID++;
     DeclTypesToEmit.push(const_cast<Decl *>(D));
+  } else if (ID < FirstDeclID && D->isChangedSinceDeserialization()) {
+    // We don't add it to the replacement collection here, because we don't
+    // have the offset yet.
+    DeclTypesToEmit.push(const_cast<Decl *>(D));
+    // Reset the flag, so that we don't add this decl multiple times.
+    const_cast<Decl *>(D)->setChangedSinceDeserialization(false);
   }
 
   return ID;
diff --git a/lib/Frontend/PCHWriterDecl.cpp b/lib/Frontend/PCHWriterDecl.cpp
index 13e5239..9893d25 100644
--- a/lib/Frontend/PCHWriterDecl.cpp
+++ b/lib/Frontend/PCHWriterDecl.cpp
@@ -1126,18 +1126,24 @@
   }
 
   // Determine the ID for this declaration
-  pch::DeclID &ID = DeclIDs[D];
-  if (ID == 0)
-    ID = NextDeclID++;
+  pch::DeclID &IDR = DeclIDs[D];
+  if (IDR == 0)
+    IDR = NextDeclID++;
+  pch::DeclID ID = IDR;
 
-  unsigned Index = ID - FirstDeclID;
+  if (ID < FirstDeclID) {
+    // We're replacing a decl in a previous file.
+    ReplacedDecls.push_back(std::make_pair(ID, Stream.GetCurrentBitNo()));
+  } else {
+    unsigned Index = ID - FirstDeclID;
 
-  // Record the offset for this declaration
-  if (DeclOffsets.size() == Index)
-    DeclOffsets.push_back(Stream.GetCurrentBitNo());
-  else if (DeclOffsets.size() < Index) {
-    DeclOffsets.resize(Index+1);
-    DeclOffsets[Index] = Stream.GetCurrentBitNo();
+    // Record the offset for this declaration
+    if (DeclOffsets.size() == Index)
+      DeclOffsets.push_back(Stream.GetCurrentBitNo());
+    else if (DeclOffsets.size() < Index) {
+      DeclOffsets.resize(Index+1);
+      DeclOffsets[Index] = Stream.GetCurrentBitNo();
+    }
   }
 
   // Build and emit a record for this declaration
@@ -1164,5 +1170,5 @@
   //
   // FIXME: This should be renamed, the predicate is much more complicated.
   if (isRequiredDecl(D, Context))
-    ExternalDefinitions.push_back(Index + 1);
+    ExternalDefinitions.push_back(ID);
 }
diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp
index e98163e..8bcb616 100644
--- a/lib/Sema/SemaDeclObjC.cpp
+++ b/lib/Sema/SemaDeclObjC.cpp
@@ -89,6 +89,9 @@
       IDecl->setLocation(AtInterfaceLoc);
       IDecl->setForwardDecl(false);
       IDecl->setClassLoc(ClassLoc);
+      // If the forward decl was in a PCH, we need to write it again in a
+      // chained PCH.
+      IDecl->setChangedSinceDeserialization(true);
       
       // Since this ObjCInterfaceDecl was created by a forward declaration,
       // we now add it to the DeclContext since it wasn't added before
@@ -176,7 +179,7 @@
     IDecl->setLocEnd(ClassLoc);
   }
 
-  /// Check then save referenced protocols.
+  // Check then save referenced protocols.
   if (NumProtoRefs) {
     IDecl->setProtocolList((ObjCProtocolDecl**)ProtoRefs, NumProtoRefs,
                            ProtoLocs, Context);
@@ -284,6 +287,9 @@
     // Make sure the cached decl gets a valid start location.
     PDecl->setLocation(AtProtoInterfaceLoc);
     PDecl->setForwardDecl(false);
+    CurContext->addDecl(PDecl);
+    // Repeat in dependent PCHs.
+    PDecl->setChangedSinceDeserialization(true);
   } else {
     PDecl = ObjCProtocolDecl::Create(Context, CurContext,
                                      AtProtoInterfaceLoc,ProtocolName);
@@ -384,13 +390,18 @@
   for (unsigned i = 0; i != NumElts; ++i) {
     IdentifierInfo *Ident = IdentList[i].first;
     ObjCProtocolDecl *PDecl = LookupProtocol(Ident, IdentList[i].second);
+    bool isNew = false;
     if (PDecl == 0) { // Not already seen?
       PDecl = ObjCProtocolDecl::Create(Context, CurContext,
                                        IdentList[i].second, Ident);
-      PushOnScopeChains(PDecl, TUScope);
+      PushOnScopeChains(PDecl, TUScope, false);
+      isNew = true;
     }
-    if (attrList)
+    if (attrList) {
       ProcessDeclAttributeList(TUScope, PDecl, attrList);
+      if (!isNew)
+        PDecl->setChangedSinceDeserialization(true);
+    }
     Protocols.push_back(PDecl);
     ProtoLocs.push_back(IdentList[i].second);
   }