Skip to content

Commit b5be80a

Browse files
committed
Remember only one @_objcImpl implementation
Simpliies the implementation dramatically.
1 parent fdb9034 commit b5be80a

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
@@ -724,11 +724,11 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
724724
llvm::PointerUnion<DeclContext *, ASTContext *> Context;
725725

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

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

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

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

1006-
Optional<Decl *> getCachedObjCInterfaceDecl() const {
1007-
if (CachedObjCInterfaceDecl == this)
997+
Optional<Decl *> getCachedObjCImplementationDecl() const {
998+
if (CachedObjCImplementationDecl == this)
1008999
return None;
1009-
return CachedObjCInterfaceDecl;
1000+
return CachedObjCImplementationDecl;
10101001
}
10111002

1012-
void setCachedObjCInterfaceDecl(Decl *decl) {
1013-
assert((CachedObjCInterfaceDecl == this)
1003+
void setCachedObjCImplementationDecl(Decl *decl) {
1004+
assert((CachedObjCImplementationDecl == this
1005+
|| CachedObjCImplementationDecl == decl)
10141006
&& "can't change CachedObjCInterfaceDecl once it's computed");
10151007
assert(decl != this && "can't form circular reference");
1016-
CachedObjCInterfaceDecl = decl;
1008+
CachedObjCImplementationDecl = decl;
10171009
}
10181010

10191011
/// 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
@@ -5003,9 +5003,8 @@ TinyPtrVector<ValueDecl *> ClangRecordMemberLookup::evaluate(
50035003
}
50045004

50055005
IterableDeclContext *IterableDeclContext::getImplementationContext() {
5006-
auto implDecls = getDecl()->getObjCImplementationDecls();
5007-
if (!implDecls.empty())
5008-
if (auto implExt = dyn_cast<ExtensionDecl>(implDecls.front()))
5006+
if (auto implDecl = getDecl()->getObjCImplementationDecl())
5007+
if (auto implExt = dyn_cast<ExtensionDecl>(implDecl))
50095008
return implExt;
50105009

50115010
return this;
@@ -5089,19 +5088,11 @@ findInterfaceGivenImpl(ClassDecl *classDecl, ExtensionDecl *ext) {
50895088
}
50905089

50915090
static ObjCInterfaceAndImplementation
5092-
cacheImplsAndConstructResult(Decl *interface,
5093-
llvm::TinyPtrVector<Decl *> impls) {
5091+
constructResult(Decl *interface, llvm::TinyPtrVector<Decl *> impls) {
50945092
if (impls.empty())
50955093
return ObjCInterfaceAndImplementation();
50965094

5097-
// Cache the array immediately so that an ArrayRef will have a stable
5098-
// address for it.
5099-
auto &importer =
5100-
ClangImporter::Implementation::get(interface->getASTContext());
5101-
auto &cachedImpls = importer.ImplementationsForObjCInterfaces[interface];
5102-
5103-
cachedImpls = std::make_unique<llvm::TinyPtrVector<Decl *>>(impls);
5104-
return ObjCInterfaceAndImplementation(interface, *cachedImpls);
5095+
return ObjCInterfaceAndImplementation(interface, impls.front());
51055096
}
51065097

51075098
static ObjCInterfaceAndImplementation
@@ -5149,7 +5140,7 @@ findContextInterfaceAndImplementation(DeclContext *dc) {
51495140
// look for extensions implementing it.
51505141

51515142
auto implDecls = findImplsGivenInterface(classDecl, categoryName);
5152-
return cacheImplsAndConstructResult(interfaceDecl, implDecls);
5143+
return constructResult(interfaceDecl, implDecls);
51535144
}
51545145

51555146
ObjCInterfaceAndImplementation ObjCInterfaceAndImplementationRequest::
@@ -5187,15 +5178,14 @@ void swift::simple_display(llvm::raw_ostream &out,
51875178
out << "clang interface ";
51885179
simple_display(out, pair.interfaceDecl);
51895180
out << " with @_objcImplementation ";
5190-
simple_display(out, pair.implementationDecls);
5191-
out << "Looking up @interface for ";
5181+
simple_display(out, pair.implementationDecl);
51925182
}
51935183

51945184
SourceLoc
51955185
swift::extractNearestSourceLoc(const ObjCInterfaceAndImplementation &pair) {
5196-
if (pair.implementationDecls.empty())
5186+
if (pair.implementationDecl)
51975187
return SourceLoc();
5198-
return extractNearestSourceLoc(pair.implementationDecls.front());
5188+
return extractNearestSourceLoc(pair.implementationDecl);
51995189
}
52005190

52015191
Decl *Decl::getObjCInterfaceDecl() const {
@@ -5208,14 +5198,14 @@ Decl *Decl::getObjCInterfaceDecl() const {
52085198
.interfaceDecl;
52095199
}
52105200

5211-
ArrayRef<Decl *> Decl::getObjCImplementationDecls() const {
5201+
Decl *Decl::getObjCImplementationDecl() const {
52125202
if (!hasClangNode())
52135203
// This *is* the implementation, if it has one.
52145204
return {};
52155205

52165206
ObjCInterfaceAndImplementationRequest req{const_cast<Decl *>(this)};
52175207
return evaluateOrDefault(getASTContext().evaluator, req, {})
5218-
.implementationDecls;
5208+
.implementationDecl;
52195209
}
52205210

52215211
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
@@ -607,15 +607,6 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
607607
}
608608

609609
public:
610-
/// Mapping from imported clang decls to native Swift implementations via
611-
/// \c \@_objcImplementation extensions. In valid code, there should be only
612-
/// one implementation per interface.
613-
///
614-
/// \c std::unique_ptr is used so that we can form a stable \c ArrayRef for
615-
/// each value vector.
616-
llvm::DenseMap<Decl *, std::unique_ptr<llvm::TinyPtrVector<Decl *>>>
617-
ImplementationsForObjCInterfaces;
618-
619610
/// Keep track of subscript declarations based on getter/setter
620611
/// pairs.
621612
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
@@ -4716,10 +4716,11 @@ llvm::GlobalValue *IRGenModule::defineTypeMetadata(
47164716
SmallVector<std::pair<Size, SILDeclRef>, 8> vtableEntries) {
47174717
assert(init);
47184718

4719-
auto isPrespecialized = concreteType->getAnyGeneric() &&
4720-
concreteType->getAnyGeneric()->isGenericContext();
4721-
auto isObjCImpl =
4722-
!concreteType->getAnyGeneric()->getObjCImplementationDecls().empty();
4719+
auto concreteTypeDecl = concreteType->getAnyGeneric();
4720+
auto isPrespecialized = concreteTypeDecl &&
4721+
concreteTypeDecl->isGenericContext();
4722+
bool isObjCImpl = concreteTypeDecl &&
4723+
concreteTypeDecl->getObjCImplementationDecl();
47234724

47244725
if (isPattern) {
47254726
assert(isConstant && "Type metadata patterns must be constant");
@@ -4834,7 +4835,7 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
48344835
} else if (nominal) {
48354836
// The symbol for native non-generic nominal type metadata is generated at
48364837
// the aliased address point (see defineTypeMetadata() above).
4837-
if (!nominal->getObjCImplementationDecls().empty()) {
4838+
if (nominal->getObjCImplementationDecl()) {
48384839
defaultVarTy = ObjCClassStructTy;
48394840
} else {
48404841
assert(!nominal->hasClangNode());
@@ -4871,7 +4872,7 @@ IRGenModule::getAddrOfTypeMetadata(CanType concreteType,
48714872
switch (canonicality) {
48724873
case TypeMetadataCanonicality::Canonical: {
48734874
auto classDecl = concreteType->getClassOrBoundGenericClass();
4874-
if (classDecl && !classDecl->getObjCImplementationDecls().empty()) {
4875+
if (classDecl && classDecl->getObjCImplementationDecl()) {
48754876
entity = LinkEntity::forObjCClass(classDecl);
48764877
} else {
48774878
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)