aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Serialization/ASTReader.h7
-rw-r--r--lib/Serialization/ASTReader.cpp7
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp58
-rw-r--r--lib/Serialization/ASTWriterDecl.cpp14
-rw-r--r--test/Index/c-index-redecls.c107
5 files changed, 181 insertions, 12 deletions
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index 23701248d0..015ccc9b3f 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -721,6 +721,13 @@ private:
/// Objective-C protocols.
std::deque<Decl *> InterestingDecls;
+ /// \brief We delay loading of the previous declaration chain to avoid
+ /// deeply nested calls when there are many redeclarations.
+ std::deque<std::pair<Decl *, serialization::DeclID> > PendingPreviousDecls;
+
+ /// \brief Ready to load the previous declaration of the given Decl.
+ void loadAndAttachPreviousDecl(Decl *D, serialization::DeclID ID);
+
/// \brief When reading a Stmt tree, Stmt operands are placed in this stack.
llvm::SmallVector<Stmt *, 16> StmtStack;
diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp
index d2167376ff..5e3fa132e6 100644
--- a/lib/Serialization/ASTReader.cpp
+++ b/lib/Serialization/ASTReader.cpp
@@ -4815,6 +4815,13 @@ void ASTReader::FinishedDeserializing() {
PendingIdentifierInfos.pop_front();
}
+ // Ready to load previous declarations of Decls that were delayed.
+ while (!PendingPreviousDecls.empty()) {
+ loadAndAttachPreviousDecl(PendingPreviousDecls.front().first,
+ PendingPreviousDecls.front().second);
+ PendingPreviousDecls.pop_front();
+ }
+
// We are not in recursive loading, so it's safe to pass the "interesting"
// decls to the consumer.
if (Consumer)
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index 56c33ca913..279d081098 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -75,6 +75,8 @@ namespace clang {
: Reader(Reader), F(F), Cursor(Cursor), ThisDeclID(thisDeclID),
Record(Record), Idx(Idx), TypeIDForTypeDecl(0) { }
+ static void attachPreviousDecl(Decl *D, Decl *previous);
+
void Visit(Decl *D);
void UpdateDecl(Decl *D, const RecordData &Record);
@@ -981,13 +983,22 @@ void ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
// can be used while this is still initializing.
assert(D->CommonOrPrev.isNull() && "getCommonPtr was called earlier on this");
- RedeclarableTemplateDecl *PrevDecl =
- cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(Record[Idx++]));
- assert((PrevDecl == 0 || PrevDecl->getKind() == D->getKind()) &&
- "PrevDecl kind mismatch");
- if (PrevDecl)
- D->CommonOrPrev = PrevDecl;
- if (PrevDecl == 0) {
+ DeclID PreviousDeclID = Record[Idx++];
+ DeclID FirstDeclID = PreviousDeclID ? Record[Idx++] : 0;
+ // We delay loading of the redeclaration chain to avoid deeply nested calls.
+ // We temporarily set the first (canonical) declaration as the previous one
+ // which is the one that matters and mark the real previous DeclID to be
+ // loaded & attached later on.
+ RedeclarableTemplateDecl *FirstDecl =
+ cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(FirstDeclID));
+ assert((FirstDecl == 0 || FirstDecl->getKind() == D->getKind()) &&
+ "FirstDecl kind mismatch");
+ if (FirstDecl) {
+ D->CommonOrPrev = FirstDecl;
+ // Mark the real previous DeclID to be loaded & attached later on.
+ if (PreviousDeclID != FirstDeclID)
+ Reader.PendingPreviousDecls.push_back(std::make_pair(D, PreviousDeclID));
+ } else {
D->CommonOrPrev = D->newCommon(*Reader.getContext());
if (RedeclarableTemplateDecl *RTD
= cast_or_null<RedeclarableTemplateDecl>(Reader.GetDecl(Record[Idx++]))) {
@@ -1224,10 +1235,20 @@ void ASTDeclReader::VisitRedeclarable(Redeclarable<T> *D) {
" reading");
case NoRedeclaration:
break;
- case PointsToPrevious:
+ case PointsToPrevious: {
+ DeclID PreviousDeclID = Record[Idx++];
+ DeclID FirstDeclID = Record[Idx++];
+ // We delay loading of the redeclaration chain to avoid deeply nested calls.
+ // We temporarily set the first (canonical) declaration as the previous one
+ // which is the one that matters and mark the real previous DeclID to be
+ // loaded & attached later on.
D->RedeclLink = typename Redeclarable<T>::PreviousDeclLink(
- cast_or_null<T>(Reader.GetDecl(Record[Idx++])));
+ cast_or_null<T>(Reader.GetDecl(FirstDeclID)));
+ if (PreviousDeclID != FirstDeclID)
+ Reader.PendingPreviousDecls.push_back(std::make_pair(static_cast<T*>(D),
+ PreviousDeclID));
break;
+ }
case PointsToLatest:
D->RedeclLink = typename Redeclarable<T>::LatestDeclLink(
cast_or_null<T>(Reader.GetDecl(Record[Idx++])));
@@ -1327,6 +1348,25 @@ ASTReader::DeclCursorForIndex(unsigned Index, DeclID ID) {
return RecordLocation(F, F->DeclOffsets[Index]);
}
+void ASTDeclReader::attachPreviousDecl(Decl *D, Decl *previous) {
+ assert(D && previous);
+ if (TagDecl *TD = dyn_cast<TagDecl>(D)) {
+ TD->RedeclLink.setPointer(cast<TagDecl>(previous));
+ } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ FD->RedeclLink.setPointer(cast<FunctionDecl>(previous));
+ } else if (VarDecl *VD = dyn_cast<VarDecl>(D)) {
+ VD->RedeclLink.setPointer(cast<VarDecl>(previous));
+ } else {
+ RedeclarableTemplateDecl *TD = cast<RedeclarableTemplateDecl>(D);
+ TD->CommonOrPrev = cast<RedeclarableTemplateDecl>(previous);
+ }
+}
+
+void ASTReader::loadAndAttachPreviousDecl(Decl *D, serialization::DeclID ID) {
+ Decl *previous = GetDecl(ID);
+ ASTDeclReader::attachPreviousDecl(D, previous);
+}
+
/// \brief Read the declaration at the given offset from the AST file.
Decl *ASTReader::ReadDeclRecord(unsigned Index, DeclID ID) {
RecordLocation Loc = DeclCursorForIndex(Index, ID);
diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp
index b0ed8bc59f..17ee85ad27 100644
--- a/lib/Serialization/ASTWriterDecl.cpp
+++ b/lib/Serialization/ASTWriterDecl.cpp
@@ -862,6 +862,9 @@ void ASTDeclWriter::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) {
// getCommonPtr() can be used while this is still initializing.
Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
+ if (D->getPreviousDeclaration())
+ Writer.AddDeclRef(D->getFirstDeclaration(), Record);
+
if (D->getPreviousDeclaration() == 0) {
// This TemplateDecl owns the CommonPtr; write it.
assert(D->isCanonicalDecl());
@@ -1075,9 +1078,14 @@ void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
if (D->RedeclLink.getNext() == D) {
Record.push_back(NoRedeclaration);
} else {
- Record.push_back(D->RedeclLink.NextIsPrevious() ? PointsToPrevious
- : PointsToLatest);
- Writer.AddDeclRef(D->RedeclLink.getPointer(), Record);
+ if (D->RedeclLink.NextIsPrevious()) {
+ Record.push_back(PointsToPrevious);
+ Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
+ Writer.AddDeclRef(D->getFirstDeclaration(), Record);
+ } else {
+ Record.push_back(PointsToLatest);
+ Writer.AddDeclRef(D->RedeclLink.getPointer(), Record);
+ }
}
T *First = D->getFirstDeclaration();
diff --git a/test/Index/c-index-redecls.c b/test/Index/c-index-redecls.c
new file mode 100644
index 0000000000..0cf2f032a3
--- /dev/null
+++ b/test/Index/c-index-redecls.c
@@ -0,0 +1,107 @@
+// RUN: %clang_cc1 -emit-pch -o %t.ast %s
+// RUN: c-index-test -test-load-tu %t.ast all
+
+// rdar://8956193 - We would blow the thread stack because of nested calls due
+// to redeclarations.
+
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);
+void socrates(void);