diff options
-rw-r--r-- | include/clang/AST/DeclBase.h | 30 | ||||
-rw-r--r-- | include/clang/Frontend/PCHBitCodes.h | 9 | ||||
-rw-r--r-- | include/clang/Frontend/PCHReader.h | 7 | ||||
-rw-r--r-- | include/clang/Frontend/PCHWriter.h | 11 | ||||
-rw-r--r-- | lib/Frontend/PCHReader.cpp | 11 | ||||
-rw-r--r-- | lib/Frontend/PCHReaderDecl.cpp | 10 | ||||
-rw-r--r-- | lib/Frontend/PCHWriter.cpp | 22 | ||||
-rw-r--r-- | lib/Frontend/PCHWriterDecl.cpp | 32 | ||||
-rw-r--r-- | lib/Sema/SemaDeclObjC.cpp | 17 | ||||
-rw-r--r-- | test/PCH/chain-predecl.h | 3 | ||||
-rw-r--r-- | test/PCH/chain-predecl.m | 16 |
11 files changed, 139 insertions, 29 deletions
diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index 8a07475800..3ff38dc0d5 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -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; } diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h index 7605670f0e..bce5c287e7 100644 --- a/include/clang/Frontend/PCHBitCodes.h +++ b/include/clang/Frontend/PCHBitCodes.h @@ -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. diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h index 7be86050ab..906e3e1b22 100644 --- a/include/clang/Frontend/PCHReader.h +++ b/include/clang/Frontend/PCHReader.h @@ -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(); diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h index 11422b4ba2..bb083eaf8f 100644 --- a/include/clang/Frontend/PCHWriter.h +++ b/include/clang/Frontend/PCHWriter.h @@ -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; diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 16afabbb7e..e55605b274 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -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; } diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp index 212eaa1332..aa5ce7aad6 100644 --- a/lib/Frontend/PCHReaderDecl.cpp +++ b/lib/Frontend/PCHReaderDecl.cpp @@ -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. diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index d7da4fc41c..a86bdb9278 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -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; diff --git a/lib/Frontend/PCHWriterDecl.cpp b/lib/Frontend/PCHWriterDecl.cpp index 13e5239fe7..9893d254e9 100644 --- a/lib/Frontend/PCHWriterDecl.cpp +++ b/lib/Frontend/PCHWriterDecl.cpp @@ -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); } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index e98163e0c4..8bcb616285 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -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 index 0000000000..bd332ff3ad --- /dev/null +++ b/test/PCH/chain-predecl.h @@ -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 index 0000000000..2b0444e15a --- /dev/null +++ b/test/PCH/chain-predecl.m @@ -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 |