Skip to content

Commit

Permalink
xxx
Browse files Browse the repository at this point in the history
  • Loading branch information
hamishknight committed May 18, 2020
1 parent bfffbbf commit 90f6e5b
Show file tree
Hide file tree
Showing 20 changed files with 442 additions and 502 deletions.
8 changes: 5 additions & 3 deletions include/swift/AST/ASTContext.h
Expand Up @@ -745,16 +745,18 @@ class ASTContext final {
/// \param isInstanceMethod Whether we are looking for an instance method
/// (vs. a class method).
///
/// \param swiftOnly If true, only loads methods from imported Swift modules,
/// skipping the Clang importer.
///
/// \param previousGeneration The previous generation with which this
/// callback was invoked. The list of methods will already contain all of
/// the results from generations up and including \c previousGeneration.
///
/// \param methods The list of @objc methods in this class that have this
/// selector and are instance/class methods as requested. This list will be
/// extended with any methods found in subsequent generations.
void loadObjCMethods(ClassDecl *classDecl,
ObjCSelector selector,
bool isInstanceMethod,
void loadObjCMethods(ClassDecl *classDecl, ObjCSelector selector,
bool isInstanceMethod, bool swiftOnly,
unsigned previousGeneration,
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods);

Expand Down
14 changes: 7 additions & 7 deletions include/swift/AST/Decl.h
Expand Up @@ -3874,13 +3874,16 @@ using AncestryOptions = OptionSet<AncestryFlags>;
/// The type of the decl itself is a MetatypeType; use getDeclaredType()
/// to get the declared type ("Complex" in the above example).
class ClassDecl final : public NominalTypeDecl {
friend class NominalTypeDecl;
friend class ObjCMethodDirectLookupRequest;

class ObjCMethodLookupTable;

SourceLoc ClassLoc;
ObjCMethodLookupTable *ObjCMethodLookup = nullptr;

/// Create the Objective-C member lookup table.
void createObjCMethodLookup();
/// Prepare the Objective-C member lookup table.
void prepareObjCMethodLookup();

struct {
/// The superclass decl and a bit to indicate whether the
Expand Down Expand Up @@ -4117,11 +4120,8 @@ class ClassDecl final : public NominalTypeDecl {
///
/// \param isInstance Whether we are looking for an instance method
/// (vs. a class method).
MutableArrayRef<AbstractFunctionDecl *> lookupDirect(ObjCSelector selector,
bool isInstance);

/// Record the presence of an @objc method with the given selector.
void recordObjCMethod(AbstractFunctionDecl *method, ObjCSelector selector);
TinyPtrVector<AbstractFunctionDecl *> lookupDirect(ObjCSelector selector,
bool isInstance) const;

/// Get all the members of this class, synthesizing any implicit members
/// that appear in the vtable if needed.
Expand Down
7 changes: 7 additions & 0 deletions include/swift/AST/Identifier.h
Expand Up @@ -891,8 +891,15 @@ class ObjCSelector {
friend bool operator>=(ObjCSelector lhs, ObjCSelector rhs) {
return lhs.compare(rhs) >= 0;
}

friend llvm::hash_code hash_value(ObjCSelector selector) {
using llvm::hash_value;
return hash_value(selector.getOpaqueValue());
}
};

void simple_display(llvm::raw_ostream &out, ObjCSelector selector);

} // end namespace swift

namespace llvm {
Expand Down
18 changes: 18 additions & 0 deletions include/swift/AST/NameLookupRequests.h
Expand Up @@ -770,6 +770,24 @@ class LookupConformanceInModuleRequest
ProtocolConformanceRef result) const;
};

class ObjCMethodDirectLookupRequest
: public SimpleRequest<ObjCMethodDirectLookupRequest,
TinyPtrVector<AbstractFunctionDecl *>(
ClassDecl *, ObjCSelector, bool),
RequestFlags::Uncached> {
public:
using SimpleRequest::SimpleRequest;

private:
friend SimpleRequest;

// Evaluation.
TinyPtrVector<AbstractFunctionDecl *> evaluate(Evaluator &evaluator,
ClassDecl *CD,
ObjCSelector selector,
bool isInstance) const;
};

#define SWIFT_TYPEID_ZONE NameLookup
#define SWIFT_TYPEID_HEADER "swift/AST/NameLookupTypeIDZone.def"
#include "swift/Basic/DefineTypeIDZone.h"
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/NameLookupTypeIDZone.def
Expand Up @@ -97,3 +97,7 @@ SWIFT_REQUEST(NameLookup, LookupPostfixOperatorRequest,
SWIFT_REQUEST(NameLookup, LookupPrecedenceGroupRequest,
PrecedenceGroupDecl *(OperatorLookupDescriptor),
Cached, NoLocationInfo)
SWIFT_REQUEST(NameLookup, ObjCMethodDirectLookupRequest,
TinyPtrVector<AbstractFunctionDecl *>(ClassDecl *, ObjCSelector,
bool),
Uncached, NoLocationInfo)
16 changes: 0 additions & 16 deletions include/swift/AST/SourceFile.h
Expand Up @@ -263,22 +263,6 @@ class SourceFile final : public FileUnit {
llvm::DenseMap<ObjCSelector, llvm::TinyPtrVector<AbstractFunctionDecl *>>
ObjCMethods;

/// List of Objective-C methods, which is used for checking unintended
/// Objective-C overrides.
std::vector<AbstractFunctionDecl *> ObjCMethodList;

/// An unsatisfied, optional @objc requirement in a protocol conformance.
using ObjCUnsatisfiedOptReq = std::pair<DeclContext *, AbstractFunctionDecl *>;

/// List of optional @objc protocol requirements that have gone
/// unsatisfied, which might conflict with other Objective-C methods.
std::vector<ObjCUnsatisfiedOptReq> ObjCUnsatisfiedOptReqs;

using ObjCMethodConflict = std::tuple<ClassDecl *, ObjCSelector, bool>;

/// List of Objective-C member conflicts we have found during type checking.
std::vector<ObjCMethodConflict> ObjCMethodConflicts;

/// Describes what kind of file this is, which can affect some type checking
/// and other behavior.
const SourceFileKind Kind;
Expand Down
12 changes: 7 additions & 5 deletions lib/AST/ASTContext.cpp
Expand Up @@ -1486,14 +1486,16 @@ void ASTContext::loadExtensions(NominalTypeDecl *nominal,
}

void ASTContext::loadObjCMethods(
ClassDecl *classDecl,
ObjCSelector selector,
bool isInstanceMethod,
unsigned previousGeneration,
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) {
ClassDecl *classDecl, ObjCSelector selector, bool isInstanceMethod,
bool swiftOnly, unsigned previousGeneration,
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) {
PrettyStackTraceSelector stackTraceSelector("looking for", selector);
PrettyStackTraceDecl stackTraceDecl("...in", classDecl);
for (auto &loader : getImpl().ModuleLoaders) {
// Ignore the Clang importer if we've been asked for Swift-only results.
if (swiftOnly && loader.get() == getClangModuleLoader())
continue;

loader->loadObjCMethods(classDecl, selector, isInstanceMethod,
previousGeneration, methods);
}
Expand Down
3 changes: 0 additions & 3 deletions lib/AST/Decl.cpp
Expand Up @@ -4274,9 +4274,6 @@ GetDestructorRequest::evaluate(Evaluator &evaluator, ClassDecl *CD) const {

// Mark DD as ObjC, as all dtors are.
DD->setIsObjC(ctx.LangOpts.EnableObjCInterop);
if (ctx.LangOpts.EnableObjCInterop)
CD->recordObjCMethod(DD, DD->getObjCSelector());

return DD;
}

Expand Down
4 changes: 3 additions & 1 deletion lib/AST/Identifier.cpp
Expand Up @@ -271,4 +271,6 @@ void ObjCSelector::dump() const {
llvm::errs() << *this << "\n";
}


void swift::simple_display(llvm::raw_ostream &out, ObjCSelector selector) {
out << "'" << selector << "'";
}
173 changes: 115 additions & 58 deletions lib/AST/NameLookup.cpp
Expand Up @@ -954,25 +954,56 @@ class swift::MemberLookupTable {
}
};

namespace {
/// Class member lookup table, which is a member lookup table with a second
/// table for lookup based on Objective-C selector.
class ClassDecl::ObjCMethodLookupTable {
using Key = std::pair<ObjCSelector, /*isInstanceMethod*/ char>;

/// Stores the set of Objective-C methods with a given selector within the
/// Objective-C method lookup table.
struct StoredObjCMethods {
struct StoredMethods {
/// The generation count at which this list was last updated.
unsigned Generation = 0;

/// The set of methods with the given selector.
llvm::TinyPtrVector<AbstractFunctionDecl *> Methods;
};
} // end anonymous namespace

/// Class member lookup table, which is a member lookup table with a second
/// table for lookup based on Objective-C selector.
class ClassDecl::ObjCMethodLookupTable
: public llvm::DenseMap<std::pair<ObjCSelector, char>,
StoredObjCMethods>
{
llvm::DenseMap<Key, StoredMethods> Table;
llvm::DenseSet<Key> LazilyCompleteNames;

public:
/// Create a new Obj-C method lookup table.
explicit ObjCMethodLookupTable(ASTContext &ctx) {
// Make sure the destructor is called when the AST context is torn down.
ctx.addCleanup([this]() {
this->~ObjCMethodLookupTable();
});
}

/// Returns \c true if the lookup table has a complete accounting of the
/// given name.
bool isLazilyComplete(ObjCSelector selector, bool isInstanceMethod) const {
return LazilyCompleteNames.count({selector, isInstanceMethod});
}

/// Mark a given lazily-loaded name as being complete.
void markLazilyComplete(ObjCSelector selector, bool isInstanceMethod) {
LazilyCompleteNames.insert({selector, isInstanceMethod});
}

/// Clears the cache of lazily-complete names. This _must_ be called when
/// new extensions with lazy members are added to the type, or lookups will
/// return inconsistent or stale results.
void clearLazilyCompleteCache() {
LazilyCompleteNames.clear();
}

void addMember(Decl *member);
void addMembers(DeclRange members);

StoredMethods &operator[](Key key) { return Table[key]; }

// Only allow allocation of member lookup tables using the allocator in
// ASTContext or by doing a placement new.
void *operator new(size_t Bytes, ASTContext &C,
Expand Down Expand Up @@ -1045,13 +1076,22 @@ void MemberLookupTable::updateLookupTable(NominalTypeDecl *nominal) {
}

void NominalTypeDecl::addedExtension(ExtensionDecl *ext) {
if (!LookupTable) return;
ClassDecl::ObjCMethodLookupTable *ObjCLookupTable = nullptr;
if (auto *CD = dyn_cast<ClassDecl>(this))
ObjCLookupTable = CD->ObjCMethodLookup;

if (ext->hasLazyMembers()) {
LookupTable->addMembers(ext->getCurrentMembersWithoutLoading());
LookupTable->clearLazilyCompleteCache();
if (LookupTable) {
LookupTable->addMembers(ext->getCurrentMembersWithoutLoading());
LookupTable->clearLazilyCompleteCache();
}
if (ObjCLookupTable)
ObjCLookupTable->clearLazilyCompleteCache();
} else {
LookupTable->addMembers(ext->getMembers());
if (LookupTable)
LookupTable->addMembers(ext->getMembers());
if (ObjCLookupTable)
ObjCLookupTable->addMembers(ext->getMembers());
}
}

Expand Down Expand Up @@ -1297,63 +1337,80 @@ DirectLookupRequest::evaluate(Evaluator &evaluator,
includeAttrImplements);
}

void ClassDecl::createObjCMethodLookup() {
assert(!ObjCMethodLookup && "Already have an Objective-C member table");
auto &ctx = getASTContext();
ObjCMethodLookup = new (ctx) ObjCMethodLookupTable();
void ClassDecl::ObjCMethodLookupTable::addMember(Decl *member) {
if (auto *storage = dyn_cast<AbstractStorageDecl>(member)) {
// If this is an @objc var, make sure to add the necessary accessors.
if (storage->isObjC()) {
storage->visitEmittedAccessors(
[&](AccessorDecl *accessor) { addMember(accessor); });
}
return;
}

// Register a cleanup with the ASTContext to call the lookup table
// destructor.
ctx.addCleanup([this]() {
this->ObjCMethodLookup->~ObjCMethodLookupTable();
});
// We're only interested in tracking @objc functions.
auto *afd = dyn_cast<AbstractFunctionDecl>(member);
if (!afd || !afd->isObjC())
return;

auto &stored = Table[{afd->getObjCSelector(), afd->isObjCInstanceMethod()}];
stored.Methods.push_back(afd);
}

MutableArrayRef<AbstractFunctionDecl *>
ClassDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
if (!ObjCMethodLookup) {
createObjCMethodLookup();
}
void ClassDecl::ObjCMethodLookupTable::addMembers(DeclRange members) {
for (auto *member : members)
addMember(member);
}

// If any modules have been loaded since we did the search last (or if we
// hadn't searched before), look in those modules, too.
auto &stored = (*ObjCMethodLookup)[{selector, isInstance}];
ASTContext &ctx = getASTContext();
if (ctx.getCurrentGeneration() > stored.Generation) {
ctx.loadObjCMethods(this, selector, isInstance, stored.Generation,
stored.Methods);
stored.Generation = ctx.getCurrentGeneration();
void ClassDecl::prepareObjCMethodLookup() {
if (ObjCMethodLookup) {
// Make sure the list of extensions is up-to-date, which may trigger the
// clearing of the lazily-complete cache.
(void)getExtensions();
return;
}

return { stored.Methods.begin(), stored.Methods.end() };
}
auto &ctx = getASTContext();
ObjCMethodLookup = new (ctx) ObjCMethodLookupTable(ctx);

void ClassDecl::recordObjCMethod(AbstractFunctionDecl *method,
ObjCSelector selector) {
if (!ObjCMethodLookup) {
createObjCMethodLookup();
}
// Pre-populate the table with any entries from the main module.
if (!hasLazyMembers())
ObjCMethodLookup->addMembers(getMembers());

// Record the method.
bool isInstanceMethod = method->isObjCInstanceMethod();
auto &vec = (*ObjCMethodLookup)[{selector, isInstanceMethod}].Methods;
for (auto *ext : getExtensions()) {
if (ext->wasDeserialized() || ext->hasClangNode())
continue;

// Check whether we have a duplicate. This only checks more than one
// element in ill-formed code, so the linear search is acceptable.
if (std::find(vec.begin(), vec.end(), method) != vec.end())
return;
ObjCMethodLookup->addMembers(ext->getMembers());
}
}

if (auto *sf = method->getParentSourceFile()) {
if (vec.size() == 1) {
// We have a conflict.
sf->ObjCMethodConflicts.push_back(std::make_tuple(this, selector,
isInstanceMethod));
} if (vec.empty()) {
sf->ObjCMethodList.push_back(method);
}
TinyPtrVector<AbstractFunctionDecl *>
ObjCMethodDirectLookupRequest::evaluate(Evaluator &evaluator, ClassDecl *CD,
ObjCSelector selector,
bool isInstance) const {
CD->prepareObjCMethodLookup();
auto &stored = (*CD->ObjCMethodLookup)[{selector, isInstance}];

// If we haven't seen this name before, or additional imported extensions have
// been bound since we last did a lookup, ask the module loaders to update the
// lookup table.
if (!CD->ObjCMethodLookup->isLazilyComplete(selector, isInstance)) {
auto &ctx = CD->getASTContext();
ctx.loadObjCMethods(CD, selector, isInstance, /*ignoreClang*/ false,
stored.Generation, stored.Methods);
stored.Generation = ctx.getCurrentGeneration();
CD->ObjCMethodLookup->markLazilyComplete(selector, isInstance);
}
return stored.Methods;
}

vec.push_back(method);
TinyPtrVector<AbstractFunctionDecl *>
ClassDecl::lookupDirect(ObjCSelector selector, bool isInstance) const {
auto &ctx = getASTContext();
auto *mutableThis = const_cast<ClassDecl *>(this);
return evaluateOrDefault(
ctx.evaluator,
ObjCMethodDirectLookupRequest{mutableThis, selector, isInstance}, {});
}

/// Determine whether the given declaration is an acceptable lookup
Expand Down

0 comments on commit 90f6e5b

Please sign in to comment.