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.

llvm-svn: 69989
This commit is contained in:
Douglas Gregor 2009-04-24 21:10:55 +00:00
parent 969bc68195
commit c78d34699f
14 changed files with 534 additions and 29 deletions

View File

@ -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.

View File

@ -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)) {

View File

@ -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.

View File

@ -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<Selector, 16> 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<uint64_t, 16> 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<ObjCMethodList, ObjCMethodList>
ReadMethodPool(Selector Sel);
void SetIdentifierInfo(unsigned ID, const IdentifierInfo *II);
/// \brief Report a diagnostic.

View File

@ -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);

View File

@ -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<ObjCMethodList, ObjCMethodList>
ReadMethodPool(Selector Sel) {
return std::pair<ObjCMethodList, ObjCMethodList>();
}
// isa/cast/dyn_cast support
static bool classof(const ExternalASTSource *Source) {

View File

@ -1084,6 +1084,114 @@ unsigned PCHStmtReader::VisitObjCProtocolExpr(ObjCProtocolExpr *E) {
// PCH reader implementation
//===----------------------------------------------------------------------===//
namespace {
class VISIBILITY_HIDDEN PCHMethodPoolLookupTrait {
PCHReader &Reader;
public:
typedef std::pair<ObjCMethodList, ObjCMethodList> 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<unsigned, unsigned>
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<IdentifierInfo *, 16> 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<ObjCMethodDecl>(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<ObjCMethodDecl>(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<PCHMethodPoolLookupTrait>
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<Decl *>(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<ObjCMethodList, ObjCMethodList>
PCHReader::ReadMethodPool(Selector Sel) {
if (!MethodPoolLookupTable)
return std::pair<ObjCMethodList, ObjCMethodList>();
// 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<ObjCMethodList, ObjCMethodList>();;
return *Pos;
}
void PCHReader::SetIdentifierInfo(unsigned ID, const IdentifierInfo *II) {
assert(ID && "Non-zero identifier ID required");
IdentifierData[ID - 1] = reinterpret_cast<uint64_t>(II);

View File

@ -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<ObjCMethodList, ObjCMethodList> 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<unsigned,unsigned>
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<PCHMethodPoolTrait> Generator;
// Create the on-disk hash table representation. Start by
// iterating through the instance method pool.
PCHMethodPoolTrait::key_type Key;
for (llvm::DenseMap<Selector, ObjCMethodList>::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<Selector, ObjCMethodList>::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<Selector, ObjCMethodList>::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<Selector, ObjCMethodList>::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<char, 4096> 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);

View File

@ -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) {

View File

@ -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 <string>
#include <vector>
@ -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<Selector, ObjCMethodList> 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<Selector, ObjCMethodList> InstanceMethodPool;
llvm::DenseMap<Selector, ObjCMethodList> 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);

View File

@ -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<ObjCMethodList, ObjCMethodList> 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<Selector, ObjCMethodList>::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<Selector, ObjCMethodList>::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<Selector, ObjCMethodList>::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<Selector, ObjCMethodList>::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

View File

@ -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));

View File

@ -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

View File

@ -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}} */