From: Douglas Gregor Date: Fri, 24 Apr 2009 21:10:55 +0000 (+0000) Subject: PCH support for the global method pool (= instance and factory method X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f0aaf7a59729a4ae0146e3464ee987745be95829;p=clang PCH support for the global method pool (= instance and factory method pools, combined). The methods in the global method pool are lazily loaded from an on-disk hash table when Sema looks into its version of the hash tables. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@69989 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index d457760118..989fdab597 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -259,6 +259,21 @@ public: } }; +/// ObjCMethodList - a linked list of methods with different signatures. +struct ObjCMethodList { + ObjCMethodDecl *Method; + ObjCMethodList *Next; + + ObjCMethodList() { + Method = 0; + Next = 0; + } + ObjCMethodList(ObjCMethodDecl *M, ObjCMethodList *C) { + Method = M; + Next = C; + } +}; + /// ObjCContainerDecl - Represents a container for method declarations. /// Current sub-classes are ObjCInterfaceDecl, ObjCCategoryDecl, and /// ObjCProtocolDecl. diff --git a/include/clang/Basic/OnDiskHashTable.h b/include/clang/Basic/OnDiskHashTable.h index 3caeb9ffd8..f54d67042c 100644 --- a/include/clang/Basic/OnDiskHashTable.h +++ b/include/clang/Basic/OnDiskHashTable.h @@ -41,6 +41,11 @@ inline unsigned BernsteinHash(const char* x, unsigned n) { return R + (R >> 5); } +inline unsigned BernsteinHashPartial(const char* x, unsigned n, unsigned R) { + for (unsigned i = 0 ; i < n ; ++i, ++x) R = R * 33 + *x; + return R + (R >> 5); +} + namespace io { typedef uint32_t Offset; @@ -199,7 +204,8 @@ public: // Store the offset for the data of this bucket. B.off = out.tell(); - + assert(B.off && "Cannot write a bucket at offset 0. Please add padding."); + // Write out the number of items in the bucket. Emit16(out, B.length); @@ -318,7 +324,7 @@ public: // Read the key. const internal_key_type& X = - Info::ReadKey((const unsigned char* const) Items, L.first); + InfoPtr->ReadKey((const unsigned char* const) Items, L.first); // If the key doesn't match just skip reading the value. if (!Info::EqualKey(X, iKey)) { diff --git a/include/clang/Frontend/PCHBitCodes.h b/include/clang/Frontend/PCHBitCodes.h index 31a30b8f4d..b84d2edecf 100644 --- a/include/clang/Frontend/PCHBitCodes.h +++ b/include/clang/Frontend/PCHBitCodes.h @@ -168,7 +168,10 @@ namespace clang { LOCALLY_SCOPED_EXTERNAL_DECLS = 11, /// \brief Record code for the Objective-C Selector Table. - SELECTOR_TABLE = 12 + SELECTOR_TABLE = 12, + + /// \brief Record code for the Objective-C method pool, + METHOD_POOL = 13 }; /// \brief Record types used within a source manager block. diff --git a/include/clang/Frontend/PCHReader.h b/include/clang/Frontend/PCHReader.h index 05e3ac6a12..34cf3e12d0 100644 --- a/include/clang/Frontend/PCHReader.h +++ b/include/clang/Frontend/PCHReader.h @@ -16,6 +16,7 @@ #include "clang/Frontend/PCHBitCodes.h" #include "clang/AST/DeclarationName.h" #include "clang/Sema/ExternalSemaSource.h" +#include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/IdentifierTable.h" @@ -148,6 +149,13 @@ private: /// \brief SelectorData, indexed by the selector ID minus one. llvm::SmallVector SelectorData; + /// \brief A pointer to an on-disk hash table of opaque type + /// PCHMethodPoolLookupTable. + /// + /// This hash table provides the instance and factory methods + /// associated with every selector known in the PCH file. + void *MethodPoolLookupTable; + /// \brief The set of external definitions stored in the the PCH /// file. llvm::SmallVector ExternalDefinitions; @@ -223,7 +231,8 @@ public: explicit PCHReader(Preprocessor &PP, ASTContext &Context) : SemaObj(0), PP(PP), Context(Context), Consumer(0), - IdentifierTableData(0), NumStatementsRead(0), NumMacrosRead(0), + IdentifierTableData(0), IdentifierLookupTable(0), + MethodPoolLookupTable(0), NumStatementsRead(0), NumMacrosRead(0), NumLexicalDeclContextsRead(0), NumVisibleDeclContextsRead(0) { } ~PCHReader() {} @@ -305,6 +314,14 @@ public: /// the macro. virtual IdentifierInfo* get(const char *NameStart, const char *NameEnd); + /// \brief Load the contents of the global method pool for a given + /// selector. + /// + /// \returns a pair of Objective-C methods lists containing the + /// instance and factory methods, respectively, with this selector. + virtual std::pair + ReadMethodPool(Selector Sel); + void SetIdentifierInfo(unsigned ID, const IdentifierInfo *II); /// \brief Report a diagnostic. diff --git a/include/clang/Frontend/PCHWriter.h b/include/clang/Frontend/PCHWriter.h index d1fe1d4f44..15c2e10bda 100644 --- a/include/clang/Frontend/PCHWriter.h +++ b/include/clang/Frontend/PCHWriter.h @@ -159,6 +159,7 @@ private: uint64_t WriteDeclContextLexicalBlock(ASTContext &Context, DeclContext *DC); uint64_t WriteDeclContextVisibleBlock(ASTContext &Context, DeclContext *DC); void WriteDeclsBlock(ASTContext &Context); + void WriteMethodPool(Sema &SemaRef); void WriteIdentifierTable(Preprocessor &PP); void WriteSelectorTable(); void WriteAttributeRecord(const Attr *Attr); diff --git a/include/clang/Sema/ExternalSemaSource.h b/include/clang/Sema/ExternalSemaSource.h index 1c216e4b86..0f0d375e9c 100644 --- a/include/clang/Sema/ExternalSemaSource.h +++ b/include/clang/Sema/ExternalSemaSource.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_SEMA_EXTERNAL_SEMA_SOURCE_H #define LLVM_CLANG_SEMA_EXTERNAL_SEMA_SOURCE_H +#include "clang/AST/DeclObjC.h" #include "clang/AST/ExternalASTSource.h" namespace clang { @@ -32,6 +33,16 @@ public: /// being used to perform semantic analysis on the abstract syntax /// tree. virtual void InitializeSema(Sema &S) {} + + /// \brief Load the contents of the global method pool for a given + /// selector. + /// + /// \returns a pair of Objective-C methods lists containing the + /// instance and factory methods, respectively, with this selector. + virtual std::pair + ReadMethodPool(Selector Sel) { + return std::pair(); + } // isa/cast/dyn_cast support static bool classof(const ExternalASTSource *Source) { diff --git a/lib/Frontend/PCHReader.cpp b/lib/Frontend/PCHReader.cpp index 62d0ba4f9f..5a1e882067 100644 --- a/lib/Frontend/PCHReader.cpp +++ b/lib/Frontend/PCHReader.cpp @@ -1084,6 +1084,114 @@ unsigned PCHStmtReader::VisitObjCProtocolExpr(ObjCProtocolExpr *E) { // PCH reader implementation //===----------------------------------------------------------------------===// +namespace { +class VISIBILITY_HIDDEN PCHMethodPoolLookupTrait { + PCHReader &Reader; + +public: + typedef std::pair data_type; + + typedef Selector external_key_type; + typedef external_key_type internal_key_type; + + explicit PCHMethodPoolLookupTrait(PCHReader &Reader) : Reader(Reader) { } + + static bool EqualKey(const internal_key_type& a, + const internal_key_type& b) { + return a == b; + } + + static unsigned ComputeHash(Selector Sel) { + unsigned N = Sel.getNumArgs(); + if (N == 0) + ++N; + unsigned R = 5381; + for (unsigned I = 0; I != N; ++I) + if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I)) + R = clang::BernsteinHashPartial(II->getName(), II->getLength(), R); + return R; + } + + // This hopefully will just get inlined and removed by the optimizer. + static const internal_key_type& + GetInternalKey(const external_key_type& x) { return x; } + + static std::pair + ReadKeyDataLength(const unsigned char*& d) { + using namespace clang::io; + unsigned KeyLen = ReadUnalignedLE16(d); + unsigned DataLen = ReadUnalignedLE16(d); + return std::make_pair(KeyLen, DataLen); + } + + internal_key_type ReadKey(const unsigned char* d, unsigned n) { + using namespace clang::io; + SelectorTable &SelTable = Reader.getContext().Selectors; + unsigned N = ReadUnalignedLE16(d); + IdentifierInfo *FirstII + = Reader.DecodeIdentifierInfo(ReadUnalignedLE32(d)); + if (N == 0) + return SelTable.getNullarySelector(FirstII); + else if (N == 1) + return SelTable.getUnarySelector(FirstII); + + llvm::SmallVector Args; + Args.push_back(FirstII); + for (unsigned I = 1; I != N; ++I) + Args.push_back(Reader.DecodeIdentifierInfo(ReadUnalignedLE32(d))); + + return SelTable.getSelector(N, &Args[0]); + } + + data_type ReadData(Selector, const unsigned char* d, unsigned DataLen) { + using namespace clang::io; + unsigned NumInstanceMethods = ReadUnalignedLE16(d); + unsigned NumFactoryMethods = ReadUnalignedLE16(d); + + data_type Result; + + // Load instance methods + ObjCMethodList *Prev = 0; + for (unsigned I = 0; I != NumInstanceMethods; ++I) { + ObjCMethodDecl *Method + = cast(Reader.GetDecl(ReadUnalignedLE32(d))); + if (!Result.first.Method) { + // This is the first method, which is the easy case. + Result.first.Method = Method; + Prev = &Result.first; + continue; + } + + Prev->Next = new ObjCMethodList(Method, 0); + Prev = Prev->Next; + } + + // Load factory methods + Prev = 0; + for (unsigned I = 0; I != NumFactoryMethods; ++I) { + ObjCMethodDecl *Method + = cast(Reader.GetDecl(ReadUnalignedLE32(d))); + if (!Result.second.Method) { + // This is the first method, which is the easy case. + Result.second.Method = Method; + Prev = &Result.second; + continue; + } + + Prev->Next = new ObjCMethodList(Method, 0); + Prev = Prev->Next; + } + + return Result; + } +}; + +} // end anonymous namespace + +/// \brief The on-disk hash table used for the global method pool. +typedef OnDiskChainedHashTable + PCHMethodPoolLookupTable; + namespace { class VISIBILITY_HIDDEN PCHIdentifierLookupTrait { PCHReader &Reader; @@ -1844,6 +1952,14 @@ PCHReader::ReadPCHBlock(uint64_t &PreprocessorBlockOffset, } LocallyScopedExternalDecls.swap(Record); break; + + case pch::METHOD_POOL: + MethodPoolLookupTable + = PCHMethodPoolLookupTable::Create( + (const unsigned char *)BlobStart + Record[0], + (const unsigned char *)BlobStart, + PCHMethodPoolLookupTrait(*this)); + break; } } Error("Premature end of bitstream"); @@ -2539,6 +2655,7 @@ Decl *PCHReader::GetDecl(pch::DeclID ID) { return 0; unsigned Index = ID - 1; + assert(Index < DeclAlreadyLoaded.size() && "Declaration ID out of range"); if (DeclAlreadyLoaded[Index]) return reinterpret_cast(DeclOffsets[Index]); @@ -2679,7 +2796,8 @@ void PCHReader::PrintStats() { void PCHReader::InitializeSema(Sema &S) { SemaObj = &S; - + S.ExternalSource = this; + // Makes sure any declarations that were deserialized "too early" // still get added to the identifier's declaration chains. for (unsigned I = 0, N = PreloadedDecls.size(); I != N; ++I) { @@ -2719,6 +2837,21 @@ IdentifierInfo* PCHReader::get(const char *NameStart, const char *NameEnd) { return *Pos; } +std::pair +PCHReader::ReadMethodPool(Selector Sel) { + if (!MethodPoolLookupTable) + return std::pair(); + + // Try to find this selector within our on-disk hash table. + PCHMethodPoolLookupTable *PoolTable + = (PCHMethodPoolLookupTable*)MethodPoolLookupTable; + PCHMethodPoolLookupTable::iterator Pos = PoolTable->find(Sel); + if (Pos == PoolTable->end()) + return std::pair();; + + return *Pos; +} + void PCHReader::SetIdentifierInfo(unsigned ID, const IdentifierInfo *II) { assert(ID && "Non-zero identifier ID required"); IdentifierData[ID - 1] = reinterpret_cast(II); diff --git a/lib/Frontend/PCHWriter.cpp b/lib/Frontend/PCHWriter.cpp index de4b9990b1..581ca051a5 100644 --- a/lib/Frontend/PCHWriter.cpp +++ b/lib/Frontend/PCHWriter.cpp @@ -1773,6 +1773,176 @@ void PCHWriter::WriteDeclsBlock(ASTContext &Context) { Stream.ExitBlock(); } +namespace { +// Trait used for the on-disk hash table used in the method pool. +class VISIBILITY_HIDDEN PCHMethodPoolTrait { + PCHWriter &Writer; + +public: + typedef Selector key_type; + typedef key_type key_type_ref; + + typedef std::pair data_type; + typedef const data_type& data_type_ref; + + explicit PCHMethodPoolTrait(PCHWriter &Writer) : Writer(Writer) { } + + static unsigned ComputeHash(Selector Sel) { + unsigned N = Sel.getNumArgs(); + if (N == 0) + ++N; + unsigned R = 5381; + for (unsigned I = 0; I != N; ++I) + if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(I)) + R = clang::BernsteinHashPartial(II->getName(), II->getLength(), R); + return R; + } + + std::pair + EmitKeyDataLength(llvm::raw_ostream& Out, Selector Sel, + data_type_ref Methods) { + unsigned KeyLen = 2 + (Sel.getNumArgs()? Sel.getNumArgs() * 4 : 4); + clang::io::Emit16(Out, KeyLen); + unsigned DataLen = 2 + 2; // 2 bytes for each of the method counts + for (const ObjCMethodList *Method = &Methods.first; Method; + Method = Method->Next) + if (Method->Method) + DataLen += 4; + for (const ObjCMethodList *Method = &Methods.second; Method; + Method = Method->Next) + if (Method->Method) + DataLen += 4; + clang::io::Emit16(Out, DataLen); + return std::make_pair(KeyLen, DataLen); + } + + void EmitKey(llvm::raw_ostream& Out, Selector Sel, unsigned) { + // FIXME: Keep track of the location of the key data (the + // selector), so we can fold the selector table's storage into + // this hash table. + unsigned N = Sel.getNumArgs(); + clang::io::Emit16(Out, N); + if (N == 0) + N = 1; + for (unsigned I = 0; I != N; ++I) + clang::io::Emit32(Out, + Writer.getIdentifierRef(Sel.getIdentifierInfoForSlot(I))); + } + + void EmitData(llvm::raw_ostream& Out, key_type_ref, + data_type_ref Methods, unsigned) { + unsigned NumInstanceMethods = 0; + for (const ObjCMethodList *Method = &Methods.first; Method; + Method = Method->Next) + if (Method->Method) + ++NumInstanceMethods; + + unsigned NumFactoryMethods = 0; + for (const ObjCMethodList *Method = &Methods.second; Method; + Method = Method->Next) + if (Method->Method) + ++NumFactoryMethods; + + clang::io::Emit16(Out, NumInstanceMethods); + clang::io::Emit16(Out, NumFactoryMethods); + for (const ObjCMethodList *Method = &Methods.first; Method; + Method = Method->Next) + if (Method->Method) + clang::io::Emit32(Out, Writer.getDeclID(Method->Method)); + clang::io::Emit16(Out, NumFactoryMethods); + for (const ObjCMethodList *Method = &Methods.second; Method; + Method = Method->Next) + if (Method->Method) + clang::io::Emit32(Out, Writer.getDeclID(Method->Method)); + } +}; +} // end anonymous namespace + +/// \brief Write the method pool into the PCH file. +/// +/// The method pool contains both instance and factory methods, stored +/// in an on-disk hash table indexed by the selector. +void PCHWriter::WriteMethodPool(Sema &SemaRef) { + using namespace llvm; + + // Create and write out the blob that contains the instance and + // factor method pools. + bool Empty = true; + { + OnDiskChainedHashTableGenerator Generator; + + // Create the on-disk hash table representation. Start by + // iterating through the instance method pool. + PCHMethodPoolTrait::key_type Key; + for (llvm::DenseMap::iterator + Instance = SemaRef.InstanceMethodPool.begin(), + InstanceEnd = SemaRef.InstanceMethodPool.end(); + Instance != InstanceEnd; ++Instance) { + // Check whether there is a factory method with the same + // selector. + llvm::DenseMap::iterator Factory + = SemaRef.FactoryMethodPool.find(Instance->first); + + if (Factory == SemaRef.FactoryMethodPool.end()) + Generator.insert(Instance->first, + std::make_pair(Instance->second, + ObjCMethodList())); + else + Generator.insert(Instance->first, + std::make_pair(Instance->second, Factory->second)); + + Empty = false; + } + + // Now iterate through the factory method pool, to pick up any + // selectors that weren't already in the instance method pool. + for (llvm::DenseMap::iterator + Factory = SemaRef.FactoryMethodPool.begin(), + FactoryEnd = SemaRef.FactoryMethodPool.end(); + Factory != FactoryEnd; ++Factory) { + // Check whether there is an instance method with the same + // selector. If so, there is no work to do here. + llvm::DenseMap::iterator Instance + = SemaRef.InstanceMethodPool.find(Factory->first); + + if (Instance == SemaRef.InstanceMethodPool.end()) + Generator.insert(Factory->first, + std::make_pair(ObjCMethodList(), Factory->second)); + + Empty = false; + } + + if (Empty) + return; + + // Create the on-disk hash table in a buffer. + llvm::SmallVector MethodPool; + uint32_t BucketOffset; + { + PCHMethodPoolTrait Trait(*this); + llvm::raw_svector_ostream Out(MethodPool); + // Make sure that no bucket is at offset 0 + clang::io::Emit16(Out, 0); + BucketOffset = Generator.Emit(Out, Trait); + } + + // Create a blob abbreviation + BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); + Abbrev->Add(BitCodeAbbrevOp(pch::METHOD_POOL)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); + Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); + unsigned MethodPoolAbbrev = Stream.EmitAbbrev(Abbrev); + + // Write the identifier table + RecordData Record; + Record.push_back(pch::METHOD_POOL); + Record.push_back(BucketOffset); + Stream.EmitRecordWithBlob(MethodPoolAbbrev, Record, + &MethodPool.front(), + MethodPool.size()); + } +} + namespace { class VISIBILITY_HIDDEN PCHIdentifierTableTrait { PCHWriter &Writer; @@ -1880,6 +2050,8 @@ void PCHWriter::WriteIdentifierTable(Preprocessor &PP) { { PCHIdentifierTableTrait Trait(*this, PP); llvm::raw_svector_ostream Out(IdentifierTable); + // Make sure that no bucket is at offset 0 + clang::io::Emit16(Out, 0); BucketOffset = Generator.Emit(Out, Trait); } @@ -2113,6 +2285,7 @@ void PCHWriter::WritePCH(Sema &SemaRef) { WritePreprocessor(PP); WriteTypesBlock(Context); WriteDeclsBlock(Context); + WriteMethodPool(SemaRef); WriteSelectorTable(); WriteIdentifierTable(PP); Stream.EmitRecord(pch::TYPE_OFFSET, TypeOffsets); diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 3702c89cf6..367c9f5756 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -163,8 +163,8 @@ void Sema::ActOnTranslationUnitScope(SourceLocation Loc, Scope *S) { Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, bool CompleteTranslationUnit) : LangOpts(pp.getLangOptions()), PP(pp), Context(ctxt), Consumer(consumer), - Diags(PP.getDiagnostics()), - SourceMgr(PP.getSourceManager()), CurContext(0), PreDeclaratorDC(0), + Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), + ExternalSource(0), CurContext(0), PreDeclaratorDC(0), CurBlock(0), PackContext(0), IdResolver(pp.getLangOptions()), GlobalNewDeleteDeclared(false), CompleteTranslationUnit(CompleteTranslationUnit) { diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index b3cb5e25fb..7426965a44 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -19,13 +19,13 @@ #include "CXXFieldCollector.h" #include "SemaOverload.h" #include "clang/AST/DeclBase.h" +#include "clang/AST/DeclObjC.h" #include "clang/Parse/Action.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/OwningPtr.h" -#include "clang/AST/DeclObjC.h" #include #include @@ -40,6 +40,7 @@ namespace clang { class Decl; class DeclContext; class DeclSpec; + class ExternalSemaSource; class NamedDecl; class Stmt; class Expr; @@ -128,6 +129,9 @@ public: Diagnostic &Diags; SourceManager &SourceMgr; + /// \brief Source of additional semantic information. + ExternalSemaSource *ExternalSource; + /// CurContext - This is the current declaration context of parsing. DeclContext *CurContext; @@ -243,27 +247,17 @@ public: /// unit. bool CompleteTranslationUnit; - /// ObjCMethodList - a linked list of methods with different signatures. - struct ObjCMethodList { - ObjCMethodDecl *Method; - ObjCMethodList *Next; - - ObjCMethodList() { - Method = 0; - Next = 0; - } - ObjCMethodList(ObjCMethodDecl *M, ObjCMethodList *C) { - Method = M; - Next = C; - } - }; + typedef llvm::DenseMap MethodPool; + /// Instance/Factory Method Pools - allows efficient lookup when typechecking /// messages to "id". We need to maintain a list, since selectors can have /// differing signatures across classes. In Cocoa, this happens to be /// extremely uncommon (only 1% of selectors are "overloaded"). - llvm::DenseMap InstanceMethodPool; - llvm::DenseMap FactoryMethodPool; + MethodPool InstanceMethodPool; + MethodPool FactoryMethodPool; + MethodPool::iterator ReadMethodPool(Selector Sel, bool isInstance); + /// Private Helper predicate to check for 'self'. bool isSelfExpr(Expr *RExpr); public: @@ -1126,6 +1120,10 @@ public: /// LookupInstanceMethodInGlobalPool - Returns the method and warns if /// there are multiple signatures. ObjCMethodDecl *LookupInstanceMethodInGlobalPool(Selector Sel, SourceRange R); + + /// LookupFactoryMethodInGlobalPool - Returns the method and warns if + /// there are multiple signatures. + ObjCMethodDecl *LookupFactoryMethodInGlobalPool(Selector Sel, SourceRange R); /// AddFactoryMethodToGlobalPool - Same as above, but for factory methods. void AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method); diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index f4014d1f25..05e471b10d 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "Sema.h" +#include "clang/Sema/ExternalSemaSource.h" #include "clang/AST/Expr.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" @@ -1090,8 +1091,47 @@ bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, return true; } +/// \brief Read the contents of the instance and factory method pools +/// for a given selector from external storage. +/// +/// This routine should only be called once, when neither the instance +/// nor the factory method pool has an entry for this selector. +Sema::MethodPool::iterator Sema::ReadMethodPool(Selector Sel, + bool isInstance) { + assert(ExternalSource && "We need an external AST source"); + assert(InstanceMethodPool.find(Sel) == InstanceMethodPool.end() && + "Selector data already loaded into the instance method pool"); + assert(FactoryMethodPool.find(Sel) == FactoryMethodPool.end() && + "Selector data already loaded into the factory method pool"); + + // Read the method list from the external source. + std::pair Methods + = ExternalSource->ReadMethodPool(Sel); + + if (isInstance) { + if (Methods.second.Method) + FactoryMethodPool[Sel] = Methods.second; + return InstanceMethodPool.insert(std::make_pair(Sel, Methods.first)).first; + } + + if (Methods.first.Method) + InstanceMethodPool[Sel] = Methods.first; + + return FactoryMethodPool.insert(std::make_pair(Sel, Methods.second)).first; +} + void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) { - ObjCMethodList &Entry = InstanceMethodPool[Method->getSelector()]; + llvm::DenseMap::iterator Pos + = InstanceMethodPool.find(Method->getSelector()); + if (Pos == InstanceMethodPool.end()) { + if (ExternalSource && !FactoryMethodPool.count(Method->getSelector())) + Pos = ReadMethodPool(Method->getSelector(), /*isInstance=*/true); + else + Pos = InstanceMethodPool.insert(std::make_pair(Method->getSelector(), + ObjCMethodList())).first; + } + + ObjCMethodList &Entry = Pos->second; if (Entry.Method == 0) { // Haven't seen a method with this selector name yet - add it. Entry.Method = Method; @@ -1113,7 +1153,16 @@ void Sema::AddInstanceMethodToGlobalPool(ObjCMethodDecl *Method) { // FIXME: Finish implementing -Wno-strict-selector-match. ObjCMethodDecl *Sema::LookupInstanceMethodInGlobalPool(Selector Sel, SourceRange R) { - ObjCMethodList &MethList = InstanceMethodPool[Sel]; + llvm::DenseMap::iterator Pos + = InstanceMethodPool.find(Sel); + if (Pos == InstanceMethodPool.end() && !FactoryMethodPool.count(Sel)) { + if (ExternalSource) + Pos = ReadMethodPool(Sel, /*isInstance=*/true); + else + return 0; + } + + ObjCMethodList &MethList = Pos->second; bool issueWarning = false; if (MethList.Method && MethList.Next) { @@ -1134,7 +1183,17 @@ ObjCMethodDecl *Sema::LookupInstanceMethodInGlobalPool(Selector Sel, } void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) { - ObjCMethodList &FirstMethod = FactoryMethodPool[Method->getSelector()]; + llvm::DenseMap::iterator Pos + = FactoryMethodPool.find(Method->getSelector()); + if (Pos == FactoryMethodPool.end()) { + if (ExternalSource && !InstanceMethodPool.count(Method->getSelector())) + Pos = ReadMethodPool(Method->getSelector(), /*isInstance=*/false); + else + Pos = FactoryMethodPool.insert(std::make_pair(Method->getSelector(), + ObjCMethodList())).first; + } + + ObjCMethodList &FirstMethod = Pos->second; if (!FirstMethod.Method) { // Haven't seen a method with this selector name yet - add it. FirstMethod.Method = Method; @@ -1156,6 +1215,37 @@ void Sema::AddFactoryMethodToGlobalPool(ObjCMethodDecl *Method) { } } +ObjCMethodDecl *Sema::LookupFactoryMethodInGlobalPool(Selector Sel, + SourceRange R) { + llvm::DenseMap::iterator Pos + = FactoryMethodPool.find(Sel); + if (Pos == FactoryMethodPool.end()) { + if (ExternalSource && !InstanceMethodPool.count(Sel)) + Pos = ReadMethodPool(Sel, /*isInstance=*/false); + else + return 0; + } + + ObjCMethodList &MethList = Pos->second; + bool issueWarning = false; + + if (MethList.Method && MethList.Next) { + for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) + // This checks if the methods differ by size & alignment. + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true)) + issueWarning = true; + } + if (issueWarning && (MethList.Method && MethList.Next)) { + Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R; + Diag(MethList.Method->getLocStart(), diag::note_using_decl) + << MethList.Method->getSourceRange(); + for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) + Diag(Next->Method->getLocStart(), diag::note_also_found_decl) + << Next->Method->getSourceRange(); + } + return MethList.Method; +} + /// ProcessPropertyDecl - Make sure that any user-defined setter/getter methods /// have the property type and issue diagnostics if they don't. /// Also synthesize a getter/setter method if none exist (and update the diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index d48ba4470c..f2cd00195f 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -497,7 +497,7 @@ Sema::ExprResult Sema::ActOnInstanceMessage(ExprTy *receiver, Selector Sel, ObjCMethodDecl *Method = LookupInstanceMethodInGlobalPool( Sel, SourceRange(lbrac,rbrac)); if (!Method) - Method = FactoryMethodPool[Sel].Method; + Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(lbrac, rbrac)); if (CheckMessageArgumentTypes(ArgExprs, NumArgs, Sel, Method, false, lbrac, rbrac, returnType)) return true; @@ -523,7 +523,7 @@ Sema::ExprResult Sema::ActOnInstanceMessage(ExprTy *receiver, Selector Sel, if (!Method) { // If not messaging 'self', look for any factory method named 'Sel'. if (!isSelfExpr(RExpr)) { - Method = FactoryMethodPool[Sel].Method; + Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(lbrac,rbrac)); if (!Method) { Method = LookupInstanceMethodInGlobalPool( Sel, SourceRange(lbrac,rbrac)); diff --git a/test/PCH/method_pool.h b/test/PCH/method_pool.h new file mode 100644 index 0000000000..f7af9044d9 --- /dev/null +++ b/test/PCH/method_pool.h @@ -0,0 +1,37 @@ +/* For use with the method_pool.m test */ + +/* Whitespace below is significant */ + + + + + + + + + + + +@interface TestMethodPool1 ++ alloc; +- (double)instMethod:(int)foo; +@end + +@interface TestMethodPool2 +- (char)instMethod:(int)foo; +@end + +@implementation TestMethodPool1 ++ alloc { +} + +- (double)instMethod:(int)foo { + return foo; +} +@end + +@implementation TestMethodPool2 +- (char)instMethod:(int)foo { + return foo; +} +@end diff --git a/test/PCH/method_pool.m b/test/PCH/method_pool.m new file mode 100644 index 0000000000..8dd7834f1d --- /dev/null +++ b/test/PCH/method_pool.m @@ -0,0 +1,21 @@ +// Test this without pch. +// RUN: clang-cc -include %S/method_pool.h -fsyntax-only -verify %s && + +// Test with pch. +// RUN: clang-cc -x=objective-c -emit-pch -o %t %S/method_pool.h && +// RUN: clang-cc -include-pch %t -fsyntax-only -verify %s + +int message_id(id x) { + return [x instMethod:17]; // expected-warning{{multiple methods}} +} + + + + + +/* Whitespace below is significant */ +/* expected-note{{using}} */ + + + +/* expected-note{{also}} */