]> granicus.if.org Git - clang/commitdiff
Instead of modifying the ObjC AST to not modify existing declarations, teach chained...
authorSebastian Redl <sebastian.redl@getdesigned.at>
Fri, 13 Aug 2010 00:28:03 +0000 (00:28 +0000)
committerSebastian Redl <sebastian.redl@getdesigned.at>
Fri, 13 Aug 2010 00:28:03 +0000 (00:28 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@110989 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclBase.h
include/clang/Frontend/PCHBitCodes.h
include/clang/Frontend/PCHReader.h
include/clang/Frontend/PCHWriter.h
lib/Frontend/PCHReader.cpp
lib/Frontend/PCHReaderDecl.cpp
lib/Frontend/PCHWriter.cpp
lib/Frontend/PCHWriterDecl.cpp
lib/Sema/SemaDeclObjC.cpp
test/PCH/chain-predecl.h [new file with mode: 0644]
test/PCH/chain-predecl.m [new file with mode: 0644]

index 8a07475800f89a0d96135590ed278d0ec12208ed..3ff38dc0d581070771771648595f8f9de61fe1d2 100644 (file)
@@ -222,11 +222,14 @@ protected:
   // NOTE: VC++ treats enums as signed, avoid using the AccessSpecifier enum
   unsigned Access : 2;
   friend class CXXClassMemberWrapper;
-  
-  // PCHLevel - the "level" of precompiled header/AST file from which this
-  // declaration was built.
-  unsigned PCHLevel : 3;
-  
+
+  /// PCHLevel - the "level" of precompiled header/AST file from which this
+  /// declaration was built.
+  unsigned PCHLevel : 2;
+
+  /// PCHChanged - if this declaration has changed since being deserialized
+  bool PCHChanged : 1;
+
   /// IdentifierNamespace - This specifies what IDNS_* namespace this lives in.
   unsigned IdentifierNamespace : 15;
 
@@ -243,7 +246,7 @@ protected:
     : NextDeclInContext(0), DeclCtx(DC),
       Loc(L), DeclKind(DK), InvalidDecl(0),
       HasAttrs(false), Implicit(false), Used(false),
-      Access(AS_none), PCHLevel(0),
+      Access(AS_none), PCHLevel(0), PCHChanged(false),
       IdentifierNamespace(getIdentifierNamespaceForKind(DK)) {
     if (Decl::CollectingStats()) add(DK);
   }
@@ -251,7 +254,7 @@ protected:
   Decl(Kind DK, EmptyShell Empty)
     : NextDeclInContext(0), DeclKind(DK), InvalidDecl(0),
       HasAttrs(false), Implicit(false), Used(false),
-      Access(AS_none), PCHLevel(0),
+      Access(AS_none), PCHLevel(0), PCHChanged(false),
       IdentifierNamespace(getIdentifierNamespaceForKind(DK)) {
     if (Decl::CollectingStats()) add(DK);
   }
@@ -358,7 +361,7 @@ public:
   unsigned getPCHLevel() const { return PCHLevel; }
 
   /// \brief The maximum PCH level that any declaration may have.
-  static const unsigned MaxPCHLevel = 7;
+  static const unsigned MaxPCHLevel = 3;
 
   /// \brief Set the PCH level of this declaration.
   void setPCHLevel(unsigned Level) { 
@@ -366,6 +369,17 @@ public:
     PCHLevel = Level;
   }
 
+  /// \brief Query whether this declaration was changed in a significant way
+  /// since being loaded from a PCH file.
+  ///
+  /// In an epic violation of layering, what is "significant" is entirely
+  /// up to the PCH system, but implemented in AST and Sema.
+  bool isChangedSinceDeserialization() const { return PCHChanged; }
+
+  /// \brief Mark this declaration as having changed since deserialization, or
+  /// reset the flag.
+  void setChangedSinceDeserialization(bool Changed) { PCHChanged = Changed; }
+
   unsigned getIdentifierNamespace() const {
     return IdentifierNamespace;
   }
index 7605670f0eaf44f3dd7339949995bd72448c6f05..bce5c287e77aea055d21010201f4a979d1ac4cfc 100644 (file)
@@ -256,7 +256,14 @@ namespace clang {
       WEAK_UNDECLARED_IDENTIFIERS = 31,
 
       /// \brief Record code for pending implicit instantiations.
-      PENDING_IMPLICIT_INSTANTIATIONS = 32
+      PENDING_IMPLICIT_INSTANTIATIONS = 32,
+
+      /// \brief Record code for a decl replacement block.
+      ///
+      /// If a declaration is modified after having been deserialized, and then
+      /// written to a dependent PCH file, its ID and offset must be added to
+      /// the replacement block.
+      DECL_REPLACEMENTS = 33
     };
 
     /// \brief Record types used within a source manager block.
index 7be86050abfc260e94244dd7d7463c1489d12941..906e3e1b2216390c9c25a7d82ca1c0b044b3aaef 100644 (file)
@@ -321,6 +321,11 @@ private:
   /// = I + 1 has already been loaded.
   std::vector<Decl *> DeclsLoaded;
 
+  typedef llvm::DenseMap<pch::DeclID, std::pair<PerFileData *, uint64_t> >
+      DeclReplacementMap;
+  /// \brief Declarations that have been replaced in a later file in the chain.
+  DeclReplacementMap ReplacedDecls;
+
   /// \brief Information about the contents of a DeclContext.
   struct DeclContextInfo {
     llvm::BitstreamCursor *Stream;
@@ -577,7 +582,7 @@ private:
   RecordLocation TypeCursorForIndex(unsigned Index);
   void LoadedDecl(unsigned Index, Decl *D);
   Decl *ReadDeclRecord(unsigned Index, pch::DeclID ID);
-  RecordLocation DeclCursorForIndex(unsigned Index);
+  RecordLocation DeclCursorForIndex(unsigned Index, pch::DeclID ID);
 
   void PassInterestingDeclsToConsumer();
 
index 11422b4ba2217bd91415aeb81be7b085300ec47e..bb083eaf8f842e1aaeb3d01c12d780963ecaa60f 100644 (file)
@@ -227,10 +227,18 @@ private:
   /// to this set, so that we can write out lexical content updates for it.
   llvm::SmallPtrSet<const NamespaceDecl *, 16> UpdatedNamespaces;
 
+  /// \brief Decls that have been replaced in the current dependent PCH.
+  ///
+  /// When a decl changes fundamentally after being deserialized (this shouldn't
+  /// happen, but the ObjC AST nodes are designed this way), it will be
+  /// serialized again. In this case, it is registered here, so that the reader
+  /// knows to read the updated version.
+  llvm::SmallVector<std::pair<pch::DeclID, uint64_t>, 16> ReplacedDecls;
+
   /// \brief Statements that we've encountered while serializing a
   /// declaration or type.
   llvm::SmallVector<Stmt *, 16> StmtsToEmit;
-  
+
   /// \brief Statements collection to use for PCHWriter::AddStmt().
   /// It will point to StmtsToEmit unless it is overriden. 
   llvm::SmallVector<Stmt *, 16> *CollectedStmts;
@@ -274,6 +282,7 @@ private:
   void WriteReferencedSelectorsPool(Sema &SemaRef);
   void WriteIdentifierTable(Preprocessor &PP);
   void WriteAttributeRecord(const Attr *Attr);
+  void WriteDeclUpdateBlock();
 
   unsigned ParmVarDeclAbbrev;
   unsigned DeclContextLexicalAbbrev;
index 16afabbb7e448cecc769b458d4453c6cc70b469d..e55605b27439b08eef31f9a9e5de3b4184b59b0e 100644 (file)
@@ -1774,6 +1774,17 @@ PCHReader::ReadPCHBlock(PerFileData &F) {
       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;
   }
index 212eaa1332c04b204989e16a07b73bdf1738a59c..aa5ce7aad6a01d5996890eedfc6e83b9439e10d9 100644 (file)
@@ -1307,7 +1307,13 @@ static bool isConsumerInterestedIn(Decl *D) {
 }
 
 /// \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 @@ PCHReader::RecordLocation PCHReader::DeclCursorForIndex(unsigned Index) {
 
 /// \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.
index d7da4fc41ce687143fb9862e1017b59fd62129fd..a86bdb9278dec8c6a1aa0883dc426229056146c8 100644 (file)
@@ -2414,6 +2414,8 @@ void PCHWriter::WritePCHChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
        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 @@ void PCHWriter::WritePCHChain(Sema &SemaRef, MemorizeStatCalls *StatCalls,
   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 @@ pch::DeclID PCHWriter::GetDeclRef(const Decl *D) {
     // 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;
index 13e5239fe78bbab07240fafe14e3baef5194014f..9893d254e9eaa56cc0da4015495860914616cf90 100644 (file)
@@ -1126,18 +1126,24 @@ void PCHWriter::WriteDecl(ASTContext &Context, Decl *D) {
   }
 
   // Determine the ID for this declaration
-  pch::DeclID &ID = DeclIDs[D];
-  if (ID == 0)
-    ID = NextDeclID++;
-
-  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();
+  pch::DeclID &IDR = DeclIDs[D];
+  if (IDR == 0)
+    IDR = NextDeclID++;
+  pch::DeclID ID = IDR;
+
+  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();
+    }
   }
 
   // Build and emit a record for this declaration
@@ -1164,5 +1170,5 @@ void PCHWriter::WriteDecl(ASTContext &Context, Decl *D) {
   //
   // FIXME: This should be renamed, the predicate is much more complicated.
   if (isRequiredDecl(D, Context))
-    ExternalDefinitions.push_back(Index + 1);
+    ExternalDefinitions.push_back(ID);
 }
index e98163e0c42bee13e5897bef9b84ceb761d49eea..8bcb616285fc3929064601de06f9d2652b7798e9 100644 (file)
@@ -89,6 +89,9 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
       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 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc,
     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 @@ Sema::ActOnStartProtocolInterface(SourceLocation AtProtoInterfaceLoc,
     // 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 @@ Sema::ActOnForwardProtocolDeclaration(SourceLocation AtProtocolLoc,
   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);
   }
diff --git a/test/PCH/chain-predecl.h b/test/PCH/chain-predecl.h
new file mode 100644 (file)
index 0000000..bd332ff
--- /dev/null
@@ -0,0 +1,3 @@
+// First header for chain-predecl.m
+@class Foo;
+@protocol Pro;
diff --git a/test/PCH/chain-predecl.m b/test/PCH/chain-predecl.m
new file mode 100644 (file)
index 0000000..2b0444e
--- /dev/null
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -emit-pch -o %t1 %S/chain-predecl.h -x objective-c
+// RUN: %clang_cc1 -emit-pch -o %t2 %s -x objective-c -include-pch %t1 -chained-pch
+
+// Test predeclarations across chained PCH.
+@interface Foo
+-(void)bar;
+@end
+@interface Boom
+-(void)bar;
+@end
+@protocol Pro
+-(void)baz;
+@end
+@protocol Kaboom
+-(void)baz;
+@end