aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/DeclBase.h30
-rw-r--r--include/clang/Frontend/PCHBitCodes.h9
-rw-r--r--include/clang/Frontend/PCHReader.h7
-rw-r--r--include/clang/Frontend/PCHWriter.h11
-rw-r--r--lib/Frontend/PCHReader.cpp11
-rw-r--r--lib/Frontend/PCHReaderDecl.cpp10
-rw-r--r--lib/Frontend/PCHWriter.cpp22
-rw-r--r--lib/Frontend/PCHWriterDecl.cpp32
-rw-r--r--lib/Sema/SemaDeclObjC.cpp17
-rw-r--r--test/PCH/chain-predecl.h3
-rw-r--r--test/PCH/chain-predecl.m16
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