Skip to content

Commit a8a614d

Browse files
committed
Warn about async-imported selector conflicts
The change in #59479 inadvertently fixed another bug in selector conflict checking: Swift did not notice when an ObjC header declared an async-imported method, but a Swift extension to the same class added another method with the same selector. Unfortunately, fixing this bug was source-breaking, and it also caused Swift to diagnose spurious conflicts between the various names that a single ObjC method might be imported with. Soften the error to a warning in Swift 5 mode and suppress the spurious diagnostics. Fixes rdar://95887113.
1 parent 4f1180d commit a8a614d

File tree

3 files changed

+37
-3
lines changed

3 files changed

+37
-3
lines changed

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2257,6 +2257,12 @@ namespace {
22572257
/// Produce a deterministic ordering of the given declarations.
22582258
struct OrderDeclarations {
22592259
bool operator()(ValueDecl *lhs, ValueDecl *rhs) const {
2260+
// If one declaration is imported from ObjC and the other is native Swift,
2261+
// put the imported Clang one first.
2262+
if (lhs->hasClangNode() != rhs->hasClangNode()) {
2263+
return lhs->hasClangNode();
2264+
}
2265+
22602266
// If the declarations come from different modules, order based on the
22612267
// module.
22622268
ModuleDecl *lhsModule = lhs->getDeclContext()->getParentModule();
@@ -2506,6 +2512,9 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
25062512
RULE(!isa<ExtensionDecl>(a->getDeclContext()),
25072513
!isa<ExtensionDecl>(b->getDeclContext()));
25082514

2515+
RULE(a->hasClangNode(),
2516+
b->hasClangNode());
2517+
25092518
// Are these from different source files? If so, fall back to the order in
25102519
// which the declarations were type checked.
25112520
// FIXME: This is gross and nondeterministic.
@@ -2534,16 +2543,28 @@ bool swift::diagnoseObjCMethodConflicts(SourceFile &sf) {
25342543
if (auto accessor = dyn_cast<AccessorDecl>(originalMethod))
25352544
originalDecl = accessor->getStorage();
25362545

2546+
// Conflicts between two imported ObjC methods are caused by the same
2547+
// clang decl having several Swift names.
2548+
if (originalDecl->hasClangNode() && conflictingDecl->hasClangNode())
2549+
continue;
2550+
2551+
bool breakingInSwift5 = false;
2552+
// Imported ObjC async methods weren't fully checked for selector
2553+
// conflicts until 5.7.
2554+
if (originalMethod->hasClangNode() && originalMethod->hasAsync())
2555+
breakingInSwift5 = true;
2556+
// Protocols weren't checked for selector conflicts in 5.0.
2557+
if (!isa<ClassDecl>(conflict.typeDecl))
2558+
breakingInSwift5 = true;
2559+
25372560
bool redeclSame = (diagInfo == origDiagInfo);
25382561
auto diag = Ctx.Diags.diagnose(conflictingDecl,
25392562
redeclSame ? diag::objc_redecl_same
25402563
: diag::objc_redecl,
25412564
diagInfo.first, diagInfo.second,
25422565
origDiagInfo.first, origDiagInfo.second,
25432566
conflict.selector);
2544-
2545-
// Protocols weren't checked for selector conflicts in 5.0.
2546-
diag.warnUntilSwiftVersionIf(!isa<ClassDecl>(conflict.typeDecl), 6);
2567+
diag.warnUntilSwiftVersionIf(breakingInSwift5, 6);
25472568

25482569
auto objcAttr = getObjCAttrIfFromAccessNote(conflictingDecl);
25492570
swift::softenIfAccessNote(conflictingDecl, objcAttr, diag);

test/Concurrency/Inputs/Delegate.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@
2121
-(void)myAsyncMethod:(void (^ _Nullable)(NSError * _Nullable, NSString * _Nullable))completionHandler;
2222
@end
2323

24+
@interface Delegate (SwiftImpls)
25+
26+
- (void)makeRequestFromSwift:(Request * _Nonnull)request completionHandler:(void (^ _Nullable)(void))handler;
27+
28+
@end
29+
2430
#endif

test/Concurrency/objc_async_overload.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,10 @@ extension Delegate {
5353
}
5454
}
5555
}
56+
57+
// rdar://95887113 - Implementing an ObjC category method in Swift is not strictly valid, but should be tolerated
58+
59+
extension Delegate {
60+
@objc public func makeRequest(fromSwift: Request, completionHandler: (() -> Void)?) {}
61+
// expected-warning@-1 {{method 'makeRequest(fromSwift:completionHandler:)' with Objective-C selector 'makeRequestFromSwift:completionHandler:' conflicts with method 'makeRequest(fromSwift:)' with the same Objective-C selector; this is an error in Swift 6}}
62+
}

0 commit comments

Comments
 (0)