Skip to content

Commit 0f7aabd

Browse files
committed
Remember only one @_objcImpl implementation
Simpliies the implementation dramatically.
1 parent 533de8a commit 0f7aabd

File tree

12 files changed

+71
-123
lines changed

12 files changed

+71
-123
lines changed

include/swift/AST/Decl.h

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -723,11 +723,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
723723
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
724724

725725
/// The imported Clang declaration representing the \c @_objcInterface for
726-
/// this declaration, or \c nullptr if there is none.
726+
/// this declaration (or vice versa), or \c nullptr if there is none.
727727
///
728728
/// If \c this (an otherwise nonsensical value), the value has not yet been
729729
/// computed.
730-
Decl *CachedObjCInterfaceDecl;
730+
Decl *CachedObjCImplementationDecl;
731731

732732
Decl(const Decl&) = delete;
733733
void operator=(const Decl&) = delete;
@@ -746,7 +746,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
746746
protected:
747747

748748
Decl(DeclKind kind, llvm::PointerUnion<DeclContext *, ASTContext *> context)
749-
: Context(context), CachedObjCInterfaceDecl(this) {
749+
: Context(context), CachedObjCImplementationDecl(this) {
750750
Bits.OpaqueBits = 0;
751751
Bits.Decl.Kind = unsigned(kind);
752752
Bits.Decl.Invalid = false;
@@ -985,34 +985,26 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
985985
/// returns the imported declaration. Otherwise return \c nullptr.
986986
///
987987
/// \seeAlso ExtensionDecl::isObjCInterface()
988-
///
989-
/// \note Currently this only links an @_objcImplementation extension to its
990-
/// class or category; in the future it should also work for members.
991988
Decl *getObjCInterfaceDecl() const;
992989

993990
/// If this is the ObjC interface of a declaration implemented in Swift,
994-
/// returns the implementating declarations. Otherwise return \c nullptr.
995-
///
996-
/// In valid code, there should only be one implementation per interface, but
997-
/// there can be several in invalid code.
991+
/// returns the implementating declaration. Otherwise return \c nullptr.
998992
///
999993
/// \seeAlso ExtensionDecl::isObjCInterface()
1000-
///
1001-
/// \note Currently this only links an imported class or category to its
1002-
/// extension; in the future it should also work for members.
1003-
ArrayRef<Decl *> getObjCImplementationDecls() const;
994+
Decl *getObjCImplementationDecl() const;
1004995

1005-
Optional<Decl *> getCachedObjCInterfaceDecl() const {
1006-
if (CachedObjCInterfaceDecl == this)
996+
Optional<Decl *> getCachedObjCImplementationDecl() const {
997+
if (CachedObjCImplementationDecl == this)
1007998
return None;
1008-
return CachedObjCInterfaceDecl;
999+
return CachedObjCImplementationDecl;
10091000
}
10101001

1011-
void setCachedObjCInterfaceDecl(Decl *decl) {
1012-
assert((CachedObjCInterfaceDecl == this)
1002+
void setCachedObjCImplementationDecl(Decl *decl) {
1003+
assert((CachedObjCImplementationDecl == this
1004+
|| CachedObjCImplementationDecl == decl)
10131005
&& "can't change CachedObjCInterfaceDecl once it's computed");
10141006
assert(decl != this && "can't form circular reference");
1015-
CachedObjCInterfaceDecl = decl;
1007+
CachedObjCImplementationDecl = decl;
10161008
}
10171009

10181010
/// Return the GenericContext if the Decl has one.

include/swift/ClangImporter/ClangImporterRequests.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,32 +234,32 @@ class ClangCategoryLookupRequest
234234
/// using \c \@_objcImplementation.
235235
struct ObjCInterfaceAndImplementation final {
236236
Decl *interfaceDecl;
237-
llvm::ArrayRef<Decl *> implementationDecls;
237+
Decl *implementationDecl;
238238

239239
ObjCInterfaceAndImplementation(Decl *interfaceDecl,
240-
llvm::ArrayRef<Decl *> implementationDecls)
241-
: interfaceDecl(interfaceDecl), implementationDecls(implementationDecls)
240+
Decl *implementationDecl)
241+
: interfaceDecl(interfaceDecl), implementationDecl(implementationDecl)
242242
{
243243
assert(interfaceDecl && "interface is non-null");
244-
assert(!implementationDecls.empty() && "implementation is non-empty");
244+
assert(implementationDecl && "implementation is non-empty");
245245
}
246246

247247
ObjCInterfaceAndImplementation()
248-
: interfaceDecl(nullptr), implementationDecls(nullptr, nullptr) {}
248+
: interfaceDecl(nullptr), implementationDecl(nullptr) {}
249249

250250
operator bool() const {
251251
return interfaceDecl;
252252
}
253253

254254
friend llvm::hash_code
255255
hash_value(const ObjCInterfaceAndImplementation &pair) {
256-
return llvm::hash_combine(pair.interfaceDecl, pair.implementationDecls);
256+
return llvm::hash_combine(pair.interfaceDecl, pair.implementationDecl);
257257
}
258258

259259
friend bool operator==(const ObjCInterfaceAndImplementation &lhs,
260260
const ObjCInterfaceAndImplementation &rhs) {
261261
return lhs.interfaceDecl == rhs.interfaceDecl
262-
&& lhs.implementationDecls == rhs.implementationDecls;
262+
&& lhs.implementationDecl == rhs.implementationDecl;
263263
}
264264

265265
friend bool operator!=(const ObjCInterfaceAndImplementation &lhs,

include/swift/IRGen/Linking.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ class LinkEntity {
15601560
private:
15611561
static bool isObjCImplementation(NominalTypeDecl *NTD) {
15621562
if (NTD)
1563-
return !NTD->getObjCImplementationDecls().empty();
1563+
return NTD->getObjCImplementationDecl();
15641564
return false;
15651565
}
15661566
static bool isObjCImplementation(CanType ty) {

lib/ClangImporter/ClangImporter.cpp

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4997,9 +4997,8 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
49974997
}
49984998

49994999
IterableDeclContext *IterableDeclContext::getImplementationContext() {
5000-
auto implDecls = getDecl()->getObjCImplementationDecls();
5001-
if (!implDecls.empty())
5002-
if (auto implExt = dyn_cast<ExtensionDecl>(implDecls.front()))
5000+
if (auto implDecl = getDecl()->getObjCImplementationDecl())
5001+
if (auto implExt = dyn_cast<ExtensionDecl>(implDecl))
50035002
return implExt;
50045003

50055004
return this;
@@ -5083,19 +5082,11 @@ findInterfaceGivenImpl(ClassDecl *classDecl, ExtensionDecl *ext) {
50835082
}
50845083

50855084
static ObjCInterfaceAndImplementation
5086-
cacheImplsAndConstructResult(Decl *interface,
5087-
llvm::TinyPtrVector<Decl *> impls) {
5085+
constructResult(Decl *interface, llvm::TinyPtrVector<Decl *> impls) {
50885086
if (impls.empty())
50895087
return ObjCInterfaceAndImplementation();
50905088

5091-
// Cache the array immediately so that an ArrayRef will have a stable
5092-
// address for it.
5093-
auto &importer =
5094-
ClangImporter::Implementation::get(interface->getASTContext());
5095-
auto &cachedImpls = importer.ImplementationsForObjCInterfaces[interface];
5096-
5097-
cachedImpls = std::make_unique<llvm::TinyPtrVector<Decl *>>(impls);
5098-
return ObjCInterfaceAndImplementation(interface, *cachedImpls);
5089+
return ObjCInterfaceAndImplementation(interface, impls.front());
50995090
}
51005091

51015092
static ObjCInterfaceAndImplementation
@@ -5143,7 +5134,7 @@ findContextInterfaceAndImplementation(DeclContext *dc) {
51435134
// look for extensions implementing it.
51445135

51455136
auto implDecls = findImplsGivenInterface(classDecl, categoryName);
5146-
return cacheImplsAndConstructResult(interfaceDecl, implDecls);
5137+
return constructResult(interfaceDecl, implDecls);
51475138
}
51485139

51495140
ObjCInterfaceAndImplementation ObjCInterfaceAndImplementationRequest::
@@ -5181,15 +5172,14 @@ void swift::simple_display(llvm::raw_ostream &out,
51815172
out << "clang interface ";
51825173
simple_display(out, pair.interfaceDecl);
51835174
out << " with @_objcImplementation ";
5184-
simple_display(out, pair.implementationDecls);
5185-
out << "Looking up @interface for ";
5175+
simple_display(out, pair.implementationDecl);
51865176
}
51875177

51885178
SourceLoc
51895179
swift::extractNearestSourceLoc(const ObjCInterfaceAndImplementation &pair) {
5190-
if (pair.implementationDecls.empty())
5180+
if (pair.implementationDecl)
51915181
return SourceLoc();
5192-
return extractNearestSourceLoc(pair.implementationDecls.front());
5182+
return extractNearestSourceLoc(pair.implementationDecl);
51935183
}
51945184

51955185
Decl *Decl::getObjCInterfaceDecl() const {
@@ -5202,14 +5192,14 @@ Decl *Decl::getObjCInterfaceDecl() const {
52025192
.interfaceDecl;
52035193
}
52045194

5205-
ArrayRef<Decl *> Decl::getObjCImplementationDecls() const {
5195+
Decl *Decl::getObjCImplementationDecl() const {
52065196
if (!hasClangNode())
52075197
// This *is* the implementation, if it has one.
52085198
return {};
52095199

52105200
ObjCInterfaceAndImplementationRequest req{const_cast<Decl *>(this)};
52115201
return evaluateOrDefault(getASTContext().evaluator, req, {})
5212-
.implementationDecls;
5202+
.implementationDecl;
52135203
}
52145204

52155205
IterableDeclContext *ClangCategoryLookupRequest::

lib/ClangImporter/ClangImporterRequests.cpp

Lines changed: 28 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -26,78 +26,52 @@ using namespace swift;
2626
Optional<ObjCInterfaceAndImplementation>
2727
ObjCInterfaceAndImplementationRequest::getCachedResult() const {
2828
auto passedDecl = std::get<0>(getStorage());
29-
Decl *interface;
29+
if (!passedDecl)
30+
return {};
3031

31-
// An imported decl can only be an interface; a native decl can only be an
32-
// implementation.
33-
if (!passedDecl || passedDecl->hasClangNode()) {
34-
interface = passedDecl;
35-
} else {
36-
if (auto cached = passedDecl->getCachedObjCInterfaceDecl())
37-
interface = *cached;
38-
else
39-
return None;
40-
}
32+
auto cachedDecl = passedDecl->getCachedObjCImplementationDecl();
4133

42-
// They passed in either null, or a Swift decl we've previously computed is
43-
// *not* an @_objcImplementation.
44-
if (!interface)
45-
return ObjCInterfaceAndImplementation();
34+
// !cachedDecl means that no decl has been cached and we need to evaluate the
35+
// request.
36+
if (!cachedDecl)
37+
return None;
4638

47-
// Okay, now find the implementations.
48-
auto &importer =
49-
ClangImporter::Implementation::get(interface->getASTContext());
50-
auto impls = importer.ImplementationsForObjCInterfaces.find(interface);
39+
// nullptr cachedDecl means that the lack of a decl was cached.
40+
else if (!*cachedDecl)
41+
return {};
5142

52-
// Uncached.
53-
if (impls == importer.ImplementationsForObjCInterfaces.end())
54-
return None;
43+
// A decl was cached! Arbitrarily guess that we looked up the implementation
44+
// from the interface.
45+
ObjCInterfaceAndImplementation result{passedDecl, *cachedDecl};
5546

56-
// They passed in a Clang decl that we've previously computed has no impls.
57-
if (!impls->second) {
58-
assert(interface == passedDecl
59-
&& "cached empty result doesn't contain the decl we looked up from???");
60-
return ObjCInterfaceAndImplementation();
61-
}
47+
// An imported decl can only be an interface; a native decl can only be an
48+
// implementation. If `implementationDecl` has a Clang node, we must have
49+
// looked up the interface from the implementation.
50+
if (result.implementationDecl->hasClangNode())
51+
std::swap(result.interfaceDecl, result.implementationDecl);
6252

63-
assert(interface == passedDecl || llvm::is_contained(*impls->second, passedDecl)
64-
&& "cached result doesn't contain the decl we looked up from???");
65-
return ObjCInterfaceAndImplementation(interface, *impls->second);
53+
return result;
6654
}
6755

6856
void ObjCInterfaceAndImplementationRequest::
6957
cacheResult(ObjCInterfaceAndImplementation value) const {
7058
Decl *decl = std::get<0>(getStorage());
71-
auto &importer =
72-
ClangImporter::Implementation::get(decl->getASTContext());
7359

7460
if (!value) {
7561
// `decl` is neither an interface nor an implementation.
76-
decl->setCachedObjCInterfaceDecl(nullptr);
77-
if (decl->hasClangNode())
78-
importer.ImplementationsForObjCInterfaces[decl] = nullptr;
79-
62+
decl->setCachedObjCImplementationDecl(nullptr);
8063
return;
8164
}
8265

83-
assert(value.interfaceDecl == decl ||
84-
llvm::is_contained(value.implementationDecls, decl)
85-
&& "ought to return the input as one of the results!");
86-
87-
// `evaluate()` already updates `importer.ImplementationsForObjCInterfaces`
88-
// so that it can return a persistent `ArrayRef` in the `value`, so we just
89-
// check that the array was cached correctly for `getCachedResult()` to find.
90-
auto &cachedImpls =
91-
importer.ImplementationsForObjCInterfaces[value.interfaceDecl];
92-
assert(cachedImpls
93-
&& cachedImpls->begin() == value.implementationDecls.begin()
94-
&& cachedImpls->end() == value.implementationDecls.end() &&
95-
"did not cache expected result?");
96-
9766
// Cache computed pointers from implementations to interfaces.
98-
value.interfaceDecl->setCachedObjCInterfaceDecl(nullptr);
99-
for (auto implDecl : value.implementationDecls)
100-
implDecl->setCachedObjCInterfaceDecl(value.interfaceDecl);
67+
value.interfaceDecl->
68+
setCachedObjCImplementationDecl(value.implementationDecl);
69+
value.implementationDecl->
70+
setCachedObjCImplementationDecl(value.interfaceDecl);
71+
72+
// If this was a duplicate implementation, cache a null so we don't recompute.
73+
if (decl != value.interfaceDecl && decl != value.implementationDecl)
74+
decl->setCachedObjCImplementationDecl(nullptr);
10175
}
10276

10377
// Define request evaluation functions for each of the name lookup requests.

lib/ClangImporter/ImporterImpl.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -606,15 +606,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
606606
}
607607

608608
public:
609-
/// Mapping from imported clang decls to native Swift implementations via
610-
/// \c \@_objcImplementation extensions. In valid code, there should be only
611-
/// one implementation per interface.
612-
///
613-
/// \c std::unique_ptr is used so that we can form a stable \c ArrayRef for
614-
/// each value vector.
615-
llvm::DenseMap<Decl *, std::unique_ptr<llvm::TinyPtrVector<Decl *>>>
616-
ImplementationsForObjCInterfaces;
617-
618609
/// Keep track of subscript declarations based on getter/setter
619610
/// pairs.
620611
llvm::DenseMap<std::pair<FuncDecl *, FuncDecl *>, SubscriptDecl *> Subscripts;

lib/IRGen/GenClass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ namespace {
161161
auto theClass = classType.getClassOrBoundGenericClass();
162162
assert(theClass);
163163

164-
if (!theClass->getObjCImplementationDecls().empty())
164+
if (theClass->getObjCImplementationDecl())
165165
Options |= ClassMetadataFlags::ClassHasObjCImplementation;
166166

167167
if (theClass->isGenericContext() && !theClass->hasClangNode())
@@ -1342,7 +1342,7 @@ namespace {
13421342
auto *theClass = getClass();
13431343

13441344
if (theClass->hasClangNode() &&
1345-
theClass->getObjCImplementationDecls().empty())
1345+
!theClass->getObjCImplementationDecl())
13461346
return IGM.getAddrOfObjCClass(theClass, NotForDefinition);
13471347

13481348
// Note that getClassMetadataStrategy() will return

lib/IRGen/GenDecl.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4701,10 +4701,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
47014701
SmallVector<std::pair<Size, SILDeclRef>, 8> vtableEntries) {
47024702
assert(init);
47034703

4704-
auto isPrespecialized = concreteType->getAnyGeneric() &&
4705-
concreteType->getAnyGeneric()->isGenericContext();
4706-
auto isObjCImpl =
4707-
!concreteType->getAnyGeneric()->getObjCImplementationDecls().empty();
4704+
auto concreteTypeDecl = concreteType->getAnyGeneric();
4705+
auto isPrespecialized = concreteTypeDecl &&
4706+
concreteTypeDecl->isGenericContext();
4707+
bool isObjCImpl = concreteTypeDecl &&
4708+
concreteTypeDecl->getObjCImplementationDecl();
47084709

47094710
if (isPattern) {
47104711
assert(isConstant && "Type metadata patterns must be constant");
@@ -4819,7 +4820,7 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
48194820
} else if (nominal) {
48204821
// The symbol for native non-generic nominal type metadata is generated at
48214822
// the aliased address point (see defineTypeMetadata() above).
4822-
if (!nominal->getObjCImplementationDecls().empty()) {
4823+
if (nominal->getObjCImplementationDecl()) {
48234824
defaultVarTy = ObjCClassStructTy;
48244825
} else {
48254826
assert(!nominal->hasClangNode());
@@ -4856,7 +4857,7 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
48564857
switch (canonicality) {
48574858
case TypeMetadataCanonicality::Canonical: {
48584859
auto classDecl = concreteType->getClassOrBoundGenericClass();
4859-
if (classDecl && !classDecl->getObjCImplementationDecls().empty()) {
4860+
if (classDecl && classDecl->getObjCImplementationDecl()) {
48604861
entity = LinkEntity::forObjCClass(classDecl);
48614862
} else {
48624863
entity = LinkEntity::forTypeMetadata(

lib/IRGen/GenMeta.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4384,7 +4384,7 @@ namespace {
43844384
static void emitObjCClassSymbol(IRGenModule &IGM,
43854385
ClassDecl *classDecl,
43864386
llvm::Constant *metadata) {
4387-
if (!classDecl->getObjCImplementationDecls().empty())
4387+
if (classDecl->getObjCImplementationDecl())
43884388
// Should already have this symbol.
43894389
return;
43904390

lib/IRGen/GenReflection.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1560,7 +1560,7 @@ void IRGenModule::emitFieldDescriptor(const NominalTypeDecl *D) {
15601560
}
15611561

15621562
if (auto *CD = dyn_cast<ClassDecl>(D)) {
1563-
if (!CD->getObjCImplementationDecls().empty())
1563+
if (CD->getObjCImplementationDecl())
15641564
needsFieldDescriptor = false;
15651565
}
15661566

lib/SIL/IR/SIL.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ FormalLinkage swift::getDeclLinkage(const ValueDecl *D) {
3636
// Clang declarations are public and can't be assured of having a
3737
// unique defining location.
3838
if (isa<ClangModuleUnit>(fileContext) &&
39-
D->getObjCImplementationDecls().empty())
39+
!D->getObjCImplementationDecl())
4040
return FormalLinkage::PublicNonUnique;
4141

4242
switch (D->getEffectiveAccess()) {

0 commit comments

Comments
 (0)