Skip to content

Commit f883fff

Browse files
committed
ClangImporter: Fix quadratic behavior with property overrides
We would add all imported members to a per-nominal vector, and then perform shadowing checks on the entire list, followed by visiting each one to look for a matching member with the right name. We were spending a lot of time inside this function as a result. Instead, change Impl.MembersForNominal to store a mapping from names to lists of members having that name, changing this into an O(1) lookup. Fixes <rdar://problem/58363207>.
1 parent c584ebd commit f883fff

File tree

2 files changed

+50
-38
lines changed

2 files changed

+50
-38
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 43 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,45 +2149,46 @@ namespace {
21492149
}
21502150
};
21512151

2152-
/// Search the member tables for this class and its superclasses and try to identify the nearest VarDecl
2153-
/// that serves as a base for an override. We have to do this ourselves because Objective-C has no
2154-
/// semantic notion of overrides, and freely allows users to refine the type of any member property in a
2155-
/// derived class.
2152+
/// Search the member tables for this class and its superclasses and try to
2153+
/// identify the nearest VarDecl that serves as a base for an override. We
2154+
/// have to do this ourselves because Objective-C has no semantic notion of
2155+
/// overrides, and freely allows users to refine the type of any member
2156+
/// property in a derived class.
21562157
///
2157-
/// The override must be the nearest possible one so there are not breaks in the override chain. That is,
2158-
/// suppose C refines B refines A and each successively redeclares a member with a different type. It
2159-
/// should be the case that the nearest override from C is B and from B is A. If the override point from
2160-
/// C were A, then B would record an override on A as well and we would introduce a semantic ambiguity.
2158+
/// The override must be the nearest possible one so there are not breaks
2159+
/// in the override chain. That is, suppose C refines B refines A and each
2160+
/// successively redeclares a member with a different type. It should be
2161+
/// the case that the nearest override from C is B and from B is A. If the
2162+
/// override point from C were A, then B would record an override on A as
2163+
/// well and we would introduce a semantic ambiguity.
21612164
///
2162-
/// There is also a special case for finding a method that stomps over a getter. If this is the case and no
2163-
/// override point is identified, we will not import the property to force users to explicitly call the method.
2165+
/// There is also a special case for finding a method that stomps over a
2166+
/// getter. If this is the case and no override point is identified, we will
2167+
/// not import the property to force users to explicitly call the method.
21642168
static std::pair<VarDecl *, bool>
2165-
identifyNearestOverridenDecl(ClangImporter::Implementation &Impl,
2166-
DeclContext *dc,
2167-
const clang::ObjCPropertyDecl *decl,
2168-
Identifier name,
2169-
ClassDecl *subject) {
2169+
identifyNearestOverriddenDecl(ClangImporter::Implementation &Impl,
2170+
DeclContext *dc,
2171+
const clang::ObjCPropertyDecl *decl,
2172+
Identifier name,
2173+
ClassDecl *subject) {
21702174
bool foundMethod = false;
21712175
for (; subject; (subject = subject->getSuperclassDecl())) {
21722176
llvm::SmallVector<ValueDecl *, 8> lookup;
2173-
auto found = Impl.MembersForNominal.find(subject);
2174-
if (found != Impl.MembersForNominal.end()) {
2175-
lookup.append(found->second.begin(), found->second.end());
2176-
namelookup::pruneLookupResultSet(dc, NL_QualifiedDefault, lookup);
2177+
auto foundNames = Impl.MembersForNominal.find(subject);
2178+
if (foundNames != Impl.MembersForNominal.end()) {
2179+
auto foundDecls = foundNames->second.find(name);
2180+
if (foundDecls != foundNames->second.end()) {
2181+
lookup.append(foundDecls->second.begin(), foundDecls->second.end());
2182+
}
21772183
}
21782184

21792185
for (auto *&result : lookup) {
2180-
// Skip declarations that don't match the name we're looking for.
2181-
if (result->getBaseName() != name)
2182-
continue;
2183-
21842186
if (auto *fd = dyn_cast<FuncDecl>(result)) {
21852187
if (fd->isInstanceMember() != decl->isInstanceProperty())
21862188
continue;
21872189

2188-
if (fd->getFullName().getArgumentNames().empty()) {
2189-
foundMethod = true;
2190-
}
2190+
assert(fd->getFullName().getArgumentNames().empty());
2191+
foundMethod = true;
21912192
} else {
21922193
auto *var = cast<VarDecl>(result);
21932194
if (var->isInstanceMember() != decl->isInstanceProperty())
@@ -2231,11 +2232,11 @@ namespace {
22312232
return getVersion() == getActiveSwiftVersion();
22322233
}
22332234

2234-
template <typename T>
2235-
T *recordMemberInContext(DeclContext *dc, T *member) {
2235+
void recordMemberInContext(DeclContext *dc, ValueDecl *member) {
22362236
assert(member && "Attempted to record null member!");
2237-
Impl.MembersForNominal[dc->getSelfNominalTypeDecl()].push_back(member);
2238-
return member;
2237+
auto *nominal = dc->getSelfNominalTypeDecl();
2238+
auto name = member->getBaseName();
2239+
Impl.MembersForNominal[nominal][name].push_back(member);
22392240
}
22402241

22412242
/// Import the name of the given entity.
@@ -3798,7 +3799,7 @@ namespace {
37983799
if (correctSwiftName)
37993800
markAsVariant(result, *correctSwiftName);
38003801

3801-
return recordMemberInContext(dc, result);
3802+
return result;
38023803
}
38033804

38043805
void finishFuncDecl(const clang::FunctionDecl *decl,
@@ -4408,7 +4409,14 @@ namespace {
44084409
}
44094410
}
44104411

4411-
return recordMemberInContext(dc, result);
4412+
// We only care about recording methods with no arguments here, because
4413+
// they can shadow imported properties.
4414+
if (!isa<AccessorDecl>(result) &&
4415+
result->getFullName().getArgumentNames().empty()) {
4416+
recordMemberInContext(dc, result);
4417+
}
4418+
4419+
return result;
44124420
}
44134421

44144422
public:
@@ -5070,7 +5078,7 @@ namespace {
50705078

50715079
bool foundMethod = false;
50725080
std::tie(overridden, foundMethod)
5073-
= identifyNearestOverridenDecl(Impl, dc, decl, name, subject);
5081+
= identifyNearestOverriddenDecl(Impl, dc, decl, name, subject);
50745082

50755083
if (foundMethod && !overridden)
50765084
return nullptr;
@@ -5165,7 +5173,8 @@ namespace {
51655173
if (correctSwiftName)
51665174
markAsVariant(result, *correctSwiftName);
51675175

5168-
return recordMemberInContext(dc, result);
5176+
recordMemberInContext(dc, result);
5177+
return result;
51695178
}
51705179

51715180
Decl *

lib/ClangImporter/ImporterImpl.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -447,10 +447,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
447447
// Mapping from imported types to their raw value types.
448448
llvm::DenseMap<const NominalTypeDecl *, Type> RawTypes;
449449

450-
/// Keep track of all member declarations that have been imported into a nominal type.
451-
llvm::DenseMap<const NominalTypeDecl *, std::vector<ValueDecl *>>
452-
MembersForNominal;
453-
454450
clang::CompilerInstance *getClangInstance() {
455451
return Instance.get();
456452
}
@@ -500,6 +496,13 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
500496
llvm::DenseMap<const NominalTypeDecl *, TinyPtrVector<ConstructorDecl *>>
501497
ConstructorsForNominal;
502498

499+
/// Keep track of all member declarations that have been imported into
500+
/// a nominal type.
501+
llvm::DenseMap<const NominalTypeDecl *,
502+
llvm::DenseMap<DeclBaseName,
503+
TinyPtrVector<ValueDecl *>>>
504+
MembersForNominal;
505+
503506
/// Keep track of the nested 'Code' enum for imported error wrapper
504507
/// structs.
505508
llvm::DenseMap<const StructDecl *, EnumDecl *> ErrorCodeEnums;

0 commit comments

Comments
 (0)