Skip to content

Commit d76a8ce

Browse files
committed
ClangImporter: Lazily load mirrored protocol members in classes
1 parent 8280b20 commit d76a8ce

8 files changed

+108
-33
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3726,26 +3726,14 @@ ClangImporter::Implementation::loadNamedMembers(
37263726
const IterableDeclContext *IDC, DeclBaseName N, uint64_t contextData) {
37273727

37283728
auto *D = IDC->getDecl();
3729-
auto *DC = cast<DeclContext>(D);
3729+
auto *DC = D->getInnermostDeclContext();
37303730
auto *CD = D->getClangDecl();
37313731
auto *CDC = cast<clang::DeclContext>(CD);
37323732
assert(CD && "loadNamedMembers on a Decl without a clangDecl");
37333733

37343734
auto *nominal = DC->getSelfNominalTypeDecl();
37353735
auto effectiveClangContext = getEffectiveClangContext(nominal);
37363736

3737-
// FIXME: The legacy of mirroring protocol members rears its ugly head,
3738-
// and as a result we have to bail on any @interface or @category that
3739-
// has a declared protocol conformance.
3740-
if (auto *ID = dyn_cast<clang::ObjCInterfaceDecl>(CD)) {
3741-
if (ID->protocol_begin() != ID->protocol_end())
3742-
return None;
3743-
}
3744-
if (auto *CCD = dyn_cast<clang::ObjCCategoryDecl>(CD)) {
3745-
if (CCD->protocol_begin() != CCD->protocol_end())
3746-
return None;
3747-
}
3748-
37493737
// There are 3 cases:
37503738
//
37513739
// - The decl is from a bridging header, CMO is Some(nullptr)
@@ -3832,6 +3820,16 @@ ClangImporter::Implementation::loadNamedMembers(
38323820
Members.push_back(cast<ValueDecl>(ctor));
38333821
}
38343822
}
3823+
3824+
if (!isa<ProtocolDecl>(D)) {
3825+
if (auto *OCD = dyn_cast<clang::ObjCContainerDecl>(CD)) {
3826+
SmallVector<Decl *, 1> newMembers;
3827+
importMirroredProtocolMembers(OCD, DC, N, newMembers);
3828+
for (auto member : newMembers)
3829+
Members.push_back(cast<ValueDecl>(member));
3830+
}
3831+
}
3832+
38353833
return Members;
38363834
}
38373835

lib/ClangImporter/ImportDecl.cpp

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4522,7 +4522,8 @@ namespace {
45224522
/// methods become class methods on NSObject).
45234523
void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl,
45244524
DeclContext *dc,
4525-
SmallVectorImpl<Decl *> &members);
4525+
Optional<DeclBaseName> name,
4526+
SmallVectorImpl<Decl *> &newMembers);
45264527

45274528
void importNonOverriddenMirroredMethods(DeclContext *dc,
45284529
MutableArrayRef<MirroredMethodEntry> entries,
@@ -6936,9 +6937,16 @@ Optional<GenericParamList *> SwiftDeclConverter::importObjCGenericParams(
69366937
genericParams, Impl.importSourceLoc(typeParamList->getRAngleLoc()));
69376938
}
69386939

6940+
void ClangImporter::Implementation::importMirroredProtocolMembers(
6941+
const clang::ObjCContainerDecl *decl, DeclContext *dc,
6942+
Optional<DeclBaseName> name, SmallVectorImpl<Decl *> &members) {
6943+
SwiftDeclConverter converter(*this, CurrentVersion);
6944+
converter.importMirroredProtocolMembers(decl, dc, name, members);
6945+
}
6946+
69396947
void SwiftDeclConverter::importMirroredProtocolMembers(
69406948
const clang::ObjCContainerDecl *decl, DeclContext *dc,
6941-
SmallVectorImpl<Decl *> &members) {
6949+
Optional<DeclBaseName> name, SmallVectorImpl<Decl *> &members) {
69426950
assert(dc);
69436951
const clang::ObjCInterfaceDecl *interfaceDecl = nullptr;
69446952
const ClangModuleUnit *declModule;
@@ -6978,24 +6986,24 @@ void SwiftDeclConverter::importMirroredProtocolMembers(
69786986

69796987
const auto &languageVersion =
69806988
Impl.SwiftContext.LangOpts.EffectiveLanguageVersion;
6981-
for (auto member : proto->getMembers()) {
6989+
auto importProtocolRequirement = [&](Decl *member) {
69826990
// Skip compatibility stubs; there's no reason to mirror them.
69836991
if (member->getAttrs().isUnavailableInSwiftVersion(languageVersion))
6984-
continue;
6992+
return;
69856993

69866994
if (auto prop = dyn_cast<VarDecl>(member)) {
69876995
auto objcProp =
69886996
dyn_cast_or_null<clang::ObjCPropertyDecl>(prop->getClangDecl());
69896997
if (!objcProp)
6990-
continue;
6998+
return;
69916999

69927000
// We can't import a property if there's already a method with this
69937001
// name. (This also covers other properties with that same name.)
69947002
// FIXME: We should still mirror the setter as a method if it's
69957003
// not already there.
69967004
clang::Selector sel = objcProp->getGetterName();
69977005
if (interfaceDecl->getInstanceMethod(sel))
6998-
continue;
7006+
return;
69997007

70007008
bool inNearbyCategory =
70017009
std::any_of(interfaceDecl->visible_categories_begin(),
@@ -7012,7 +7020,7 @@ void SwiftDeclConverter::importMirroredProtocolMembers(
70127020
return category->getInstanceMethod(sel);
70137021
});
70147022
if (inNearbyCategory)
7015-
continue;
7023+
return;
70167024

70177025
if (auto imported =
70187026
Impl.importMirroredDecl(objcProp, dc, getVersion(), proto)) {
@@ -7021,24 +7029,38 @@ void SwiftDeclConverter::importMirroredProtocolMembers(
70217029
// metatype.
70227030
}
70237031

7024-
continue;
7032+
return;
70257033
}
70267034

70277035
auto afd = dyn_cast<AbstractFunctionDecl>(member);
70287036
if (!afd)
7029-
continue;
7037+
return;
70307038

70317039
if (isa<AccessorDecl>(afd))
7032-
continue;
7040+
return;
70337041

70347042
auto objcMethod =
70357043
dyn_cast_or_null<clang::ObjCMethodDecl>(member->getClangDecl());
70367044
if (!objcMethod)
7037-
continue;
7045+
return;
70387046

70397047
// For now, just remember that we saw this method.
70407048
methodsByName[objcMethod->getSelector()]
70417049
.push_back(MirroredMethodEntry{objcMethod, proto});
7050+
};
7051+
7052+
if (name) {
7053+
// If we're asked to import a specific name only, look for that in the
7054+
// protocol.
7055+
auto results = proto->lookupDirect(*name);
7056+
for (auto *member : results)
7057+
if (member->getDeclContext() == proto)
7058+
importProtocolRequirement(member);
7059+
7060+
} else {
7061+
// Otherwise, import all mirrored members.
7062+
for (auto *member : proto->getMembers())
7063+
importProtocolRequirement(member);
70427064
}
70437065
}
70447066

@@ -8680,18 +8702,25 @@ void ClangImporter::Implementation::collectMembersToAdd(
86808702
insertMembersAndAlternates(nd, members);
86818703
}
86828704

8683-
SwiftDeclConverter converter(*this, CurrentVersion);
8705+
// Objective-C protocols don't require any special handling.
8706+
if (isa<clang::ObjCProtocolDecl>(objcContainer))
8707+
return;
8708+
8709+
// Objective-C interfaces can inherit constructors from their superclass,
8710+
// which we must model explicitly.
86848711
if (auto clangClass = dyn_cast<clang::ObjCInterfaceDecl>(objcContainer)) {
86858712
objcContainer = clangClass = clangClass->getDefinition();
86868713
importInheritedConstructors(clangClass, cast<ClassDecl>(D), members);
86878714
} else if (auto clangProto
86888715
= dyn_cast<clang::ObjCProtocolDecl>(objcContainer)) {
86898716
objcContainer = clangProto->getDefinition();
86908717
}
8691-
// Import mirrored declarations for protocols to which this category
8692-
// or extension conforms.
8718+
8719+
// Interfaces and categories can declare protocol conformances, and
8720+
// members of those protocols are mirrored into the interface or
8721+
// category.
86938722
// FIXME: This is supposed to be a short-term hack.
8694-
converter.importMirroredProtocolMembers(objcContainer, DC, members);
8723+
importMirroredProtocolMembers(objcContainer, DC, None, members);
86958724
}
86968725

86978726
void ClangImporter::Implementation::loadAllConformances(

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
818818
void importInheritedConstructors(const clang::ObjCInterfaceDecl *curObjCClass,
819819
const ClassDecl *classDecl,
820820
SmallVectorImpl<Decl *> &newMembers);
821-
821+
void importMirroredProtocolMembers(const clang::ObjCContainerDecl *decl,
822+
DeclContext *dc, Optional<DeclBaseName> name,
823+
SmallVectorImpl<Decl *> &members);
824+
822825
/// Utility function for building simple generic signatures.
823826
GenericSignature buildGenericSignature(GenericParamList *genericParams,
824827
DeclContext *dc);

test/ClangImporter/objc_init_redundant.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ extension NSObject {
1616
extension NSObject {
1717
@objc(class) func foo() { } // expected-error{{method 'foo()' with Objective-C selector 'class' conflicts with method 'class()' with the same Objective-C selector}}
1818
// CHECK: objc_init_redundant.swift:[[@LINE-1]]:21: error: method 'foo()' with Objective-C selector 'class' conflicts
19-
// CHECK: ObjectiveC.NSObject:{{.*}}note: method 'class()' declared here
19+
// CHECK: ObjectiveC.NSObjectProtocol:{{.*}}note: method 'class()' declared here
2020
}
2121

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
Name: NamedLazyMembers
3+
Classes:
4+
- Name: PrivateDoer
5+
Methods:
6+
- Selector: "objectForKey:"
7+
MethodKind: Instance
8+
SwiftPrivate: true
9+
- Selector: "count"
10+
MethodKind: Instance
11+
SwiftPrivate: true

test/NameBinding/Inputs/NamedLazyMembers/NamedLazyMembers.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
- (void)simplyDoSomeWorkWithSpeed:(int)s alacrity:(int)a
4040
NS_SWIFT_NAME(simplyDoSomeWorkWithSpeed(speed:levelOfAlacrity:));
4141

42+
// Make sure that swift_private correctly adds the '__' prefix.
43+
- (void)count __attribute__((swift_private));
44+
- (void)objectForKey:(NSObject *)key __attribute__((swift_private));
45+
4246
// These we are generally trying to not-import, via laziness.
4347
- (void)simplyGoForWalk;
4448
- (void)simplyTakeNap;
@@ -72,6 +76,7 @@
7276
- (void)categoricallyReadBook;
7377
- (void)categoricallyAttendLecture;
7478
- (void)categoricallyWriteLetter;
79+
7580
@end
7681

7782

@@ -120,3 +125,16 @@
120125
- (void)exuberantlyAttendLecture;
121126
- (void)exuberantlyWriteLetter;
122127
@end
128+
129+
@protocol PrivateMethods <NSObject>
130+
- (void)count;
131+
- (void)objectForKey:(NSObject *)key;
132+
@end
133+
134+
@interface PrivateDoer
135+
- (void)count;
136+
- (void)objectForKey:(NSObject *)key;
137+
@end
138+
139+
@interface PrivateDoer(Category) <PrivateMethods>
140+
@end

test/NameBinding/named_lazy_member_loading_objc_interface.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public func foo() {
2323
let _ = d.simplyDoSomeWork(withSpeed:10)
2424
let _ = d.simplyDoVeryImportantWork(speed:10, thoroughness:12)
2525
let _ = d.simplyDoSomeWorkWithSpeed(speed:10, levelOfAlacrity:12)
26+
let _ = d.__count
27+
let _ = d.__object(forKey: nil)
2628
}
2729

2830
// Make sure that simply subclassing an imported subclass doesn't page in all

test/NameBinding/named_lazy_member_loading_protocol_mirroring.swift

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
// RUN: rm -rf %t && mkdir -p %t/stats-pre && mkdir -p %t/stats-post
44
//
55
// Prime module cache
6-
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -typecheck %s
6+
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -typecheck %s -swift-version 5
77
//
88
// Check that named-lazy-member-loading reduces the number of Decls deserialized
9-
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s
10-
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s
9+
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -disable-named-lazy-member-loading -stats-output-dir %t/stats-pre %s -swift-version 5
10+
// RUN: %target-swift-frontend -typecheck -I %S/Inputs/NamedLazyMembers -stats-output-dir %t/stats-post %s -swift-version 5
11+
// RUN: %{python} %utils/process-stats-dir.py --evaluate-delta 'NumTotalClangImportedEntities < -10' %t/stats-pre %t/stats-post
1112

1213
import NamedLazyMembers
1314

@@ -24,3 +25,16 @@ public func foo(d: DerivedFromMirroringDoer) {
2425
let _ = d.mirroredBaseInstanceMethod()
2526
let _ = d.mirroredDerivedInstanceMethod()
2627
}
28+
29+
extension PrivateDoer {
30+
public var count: Int { return 0 }
31+
public func object(forKey: NSObject?) {}
32+
}
33+
34+
public func foo(d: PrivateDoer) {
35+
_ = d.count
36+
_ = d.object
37+
38+
let _ = d.__count
39+
let _ = d.__object(forKey: nil)
40+
}

0 commit comments

Comments
 (0)