diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 8b3b0b9fb9a4..4c1fefef503e 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -967,6 +967,7 @@ protected: mutable Decl *LastDecl; friend class ExternalASTSource; + friend class ASTWriter; /// \brief Build up a chain of declarations. /// diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index d0cdbeabbb56..9baaf4bcb527 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -1466,6 +1466,9 @@ public: void LoadMacroDefinition( llvm::DenseMap::iterator Pos); + /// \brief Load all external visible decls in the given DeclContext. + void completeVisibleDeclsMap(DeclContext *DC); + /// \brief Retrieve the AST context that this AST reader supplements. ASTContext &getContext() { return Context; } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 4c6c9dc524c4..9436bfb50bd3 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -4972,6 +4972,51 @@ ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, return const_cast(DC)->lookup(Name); } +namespace { + /// \brief ModuleFile visitor used to complete the visible decls map of a + /// declaration context. + class DeclContextVisibleDeclMapVisitor { + ASTReader &Reader; + DeclContext *DC; + + public: + DeclContextVisibleDeclMapVisitor(ASTReader &Reader, DeclContext *DC) + : Reader(Reader), DC(DC) { } + + static bool visit(ModuleFile &M, void *UserData) { + return static_cast(UserData)->visit(M); + } + + bool visit(ModuleFile &M) { + // Check whether we have any visible declaration information for + // this context in this module. + ModuleFile::DeclContextInfosMap::iterator + Info = M.DeclContextInfos.find(DC); + if (Info == M.DeclContextInfos.end() || + !Info->second.NameLookupTableData) + return false; + + // Look for this name within this module. + ASTDeclContextNameLookupTable *LookupTable = + (ASTDeclContextNameLookupTable*)Info->second.NameLookupTableData; + for (ASTDeclContextNameLookupTable::key_iterator + I = LookupTable->key_begin(), + E = LookupTable->key_end(); I != E; ++I) { + DC->lookup(*I); // Force loading of the visible decls for the decl name. + } + + return false; + } + }; +} + +void ASTReader::completeVisibleDeclsMap(DeclContext *DC) { + if (!DC->hasExternalVisibleStorage()) + return; + DeclContextVisibleDeclMapVisitor Visitor(*this, DC); + ModuleMgr.visit(&DeclContextVisibleDeclMapVisitor::visit, &Visitor); +} + /// \brief Under non-PCH compilation the consumer receives the objc methods /// before receiving the implementation, and codegen depends on this. /// We simulate this by deserializing and passing to consumer the methods of the diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index d7cc850a809e..7a4ef63bb3f6 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/Serialization/ASTWriter.h" +#include "clang/Serialization/ASTReader.h" #include "ASTCommon.h" #include "clang/AST/DeclVisitor.h" #include "clang/AST/DeclCXX.h" @@ -1640,19 +1641,6 @@ void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) { RecordData Record; ASTDeclWriter W(*this, Context, Record); - // If this declaration is also a DeclContext, write blocks for the - // declarations that lexically stored inside its context and those - // declarations that are visible from its context. These blocks - // are written before the declaration itself so that we can put - // their offsets into the record for the declaration. - uint64_t LexicalOffset = 0; - uint64_t VisibleOffset = 0; - DeclContext *DC = dyn_cast(D); - if (DC) { - LexicalOffset = WriteDeclContextLexicalBlock(Context, DC); - VisibleOffset = WriteDeclContextVisibleBlock(Context, DC); - } - // Determine the ID for this declaration. serialization::DeclID ID; if (D->isFromASTFile()) @@ -1664,8 +1652,31 @@ void ASTWriter::WriteDecl(ASTContext &Context, Decl *D) { ID= IDR; } + + bool isReplacingADecl = ID < FirstDeclID; + + // If this declaration is also a DeclContext, write blocks for the + // declarations that lexically stored inside its context and those + // declarations that are visible from its context. These blocks + // are written before the declaration itself so that we can put + // their offsets into the record for the declaration. + uint64_t LexicalOffset = 0; + uint64_t VisibleOffset = 0; + DeclContext *DC = dyn_cast(D); + if (DC) { + if (isReplacingADecl) { + // It is replacing a decl from a chained PCH; make sure that the + // DeclContext is fully loaded. + if (DC->hasExternalLexicalStorage()) + DC->LoadLexicalDeclsFromExternalStorage(); + if (DC->hasExternalVisibleStorage()) + Chain->completeVisibleDeclsMap(DC); + } + LexicalOffset = WriteDeclContextLexicalBlock(Context, DC); + VisibleOffset = WriteDeclContextVisibleBlock(Context, DC); + } - if (ID < FirstDeclID) { + if (isReplacingADecl) { // We're replacing a decl in a previous file. ReplacedDecls.push_back(ReplacedDeclInfo(ID, Stream.GetCurrentBitNo(), D->getLocation())); diff --git a/clang/test/PCH/chain-class-extension.m b/clang/test/PCH/chain-class-extension.m new file mode 100644 index 000000000000..c99d6d43ae5e --- /dev/null +++ b/clang/test/PCH/chain-class-extension.m @@ -0,0 +1,42 @@ +// Without PCH +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin10 -fobjc-arc %s -include %s -include %s + +// With PCH +// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-apple-darwin10 -fobjc-arc %s -chain-include %s -chain-include %s + +#ifndef HEADER1 +#define HEADER1 +//===----------------------------------------------------------------------===// +// Primary header + +@interface I ++(void)meth; +@end + +//===----------------------------------------------------------------------===// +#elif !defined(HEADER2) +#define HEADER2 +#if !defined(HEADER1) +#error Header inclusion order messed up +#endif + +//===----------------------------------------------------------------------===// +// Dependent header + +@interface I() +@property (assign) id prop; ++(void)meth2; +@end + +//===----------------------------------------------------------------------===// +#else +//===----------------------------------------------------------------------===// + +void foo(I *i) { + [I meth]; + [I meth2]; + i.prop = 0; +} + +//===----------------------------------------------------------------------===// +#endif