Skip to content

Commit 4c5c674

Browse files
committed
Allow same-selector async/completion requirements
An @objc protocol can now explicitly declare both the `async` and completion handler signatures of a method as long as the completion handler one is marked with `@available(*, renamed:)`. This is not yet handled correctly in PrintAsClang.
1 parent 77626f4 commit 4c5c674

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

lib/AST/NameLookup.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,16 @@ NominalTypeDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
16991699
return stored.Methods;
17001700
}
17011701

1702+
/// Is the new method an async alternative of any existing method, or vice
1703+
/// versa?
1704+
static bool isAnAsyncAlternative(AbstractFunctionDecl *newDecl,
1705+
llvm::TinyPtrVector<AbstractFunctionDecl *> &vec) {
1706+
return llvm::any_of(vec, [&](AbstractFunctionDecl *oldDecl) {
1707+
return newDecl->getAsyncAlternative(/*isKnownObjC=*/true) == oldDecl
1708+
|| oldDecl->getAsyncAlternative(/*isKnownObjC=*/true) == newDecl;
1709+
});
1710+
}
1711+
17021712
void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
17031713
ObjCSelector selector) {
17041714
if (!ObjCMethodLookup && !createObjCMethodLookup())
@@ -1716,7 +1726,7 @@ void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
17161726
if (auto *sf = method->getParentSourceFile()) {
17171727
if (vec.empty()) {
17181728
sf->ObjCMethodList.push_back(method);
1719-
} else {
1729+
} else if (!isa<ProtocolDecl>(this) || !isAnAsyncAlternative(method, vec)) {
17201730
// We have a conflict.
17211731
sf->ObjCMethodConflicts.insert({ this, selector, isInstanceMethod });
17221732
}

test/ClangImporter/objc_async_conformance.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,33 @@ class SelectorOK2 : NSObject, RequiredObserverOnlyCompletion {
6363
@nonobjc func hello(_ completion : @escaping (Bool) -> Void) -> Void { completion(true) }
6464
}
6565

66+
// can declare an @objc protocol with both selectors...
67+
@objc protocol SelectorBothAsyncProto {
68+
@objc(helloWithCompletion:)
69+
func hello() async -> Bool
70+
71+
@available(*, renamed: "hello()")
72+
@objc(helloWithCompletion:)
73+
func hello(completion: @escaping (Bool) -> Void)
74+
}
75+
76+
// and conform by implementing either one...
77+
class SelectorBothAsync1: NSObject, SelectorBothAsyncProto {
78+
func hello() async -> Bool { true }
79+
}
80+
class SelectorBothAsync2: NSObject, SelectorBothAsyncProto {
81+
func hello(completion: @escaping (Bool) -> Void) { completion(true) }
82+
}
83+
84+
// but not without declaring the async alternative.
85+
@objc protocol BadSelectorBothAsyncProto {
86+
@objc(helloWithCompletion:)
87+
func hello() async -> Bool // expected-note {{method 'hello()' declared here}}
88+
89+
@objc(helloWithCompletion:)
90+
func hello(completion: @escaping (Bool) -> Void) // expected-warning {{method 'hello(completion:)' with Objective-C selector 'helloWithCompletion:' conflicts with method 'hello()' with the same Objective-C selector; this is an error in Swift 6}}
91+
}
92+
6693
// additional coverage for situation like C4, where the method names don't
6794
// clash on the ObjC side, but they do on Swift side, BUT their ObjC selectors
6895
// differ, so it's OK.

0 commit comments

Comments
 (0)