Skip to content

Commit b2d29fd

Browse files
committed
Further loosen selector conflict checks
Some mixed-language projects import Objective-C headers through their umbrella or bridging header that declarate things that are actually implemented in Swift. This isn’t something we really supported or had tests for, but it happens in practice and we can’t break them. Carve out a second exception to method conflict checking for when all of the conflicting methods are imported ObjC methods in either the same module or a bridging header. Fixes rdar://96470068.
1 parent 4f1180d commit b2d29fd

File tree

6 files changed

+59
-10
lines changed

6 files changed

+59
-10
lines changed

lib/AST/NameLookup.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "swift/Basic/Statistic.h"
3737
#include "swift/ClangImporter/ClangImporterRequests.h"
3838
#include "swift/Parse/Lexer.h"
39+
#include "swift/Strings.h"
3940
#include "clang/AST/DeclObjC.h"
4041
#include "clang/Basic/Specifiers.h"
4142
#include "llvm/ADT/DenseMap.h"
@@ -1716,14 +1717,34 @@ NominalTypeDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
17161717
return stored.Methods;
17171718
}
17181719

1719-
/// Is the new method an async alternative of any existing method, or vice
1720-
/// versa?
1721-
static bool isAnAsyncAlternative(AbstractFunctionDecl *newDecl,
1722-
llvm::TinyPtrVector<AbstractFunctionDecl *> &vec) {
1723-
return llvm::any_of(vec, [&](AbstractFunctionDecl *oldDecl) {
1720+
/// If there is an apparent conflict between \p newDecl and one of the methods
1721+
/// in \p vec, should we diagnose it?
1722+
static bool
1723+
shouldDiagnoseConflict(NominalTypeDecl *ty, AbstractFunctionDecl *newDecl,
1724+
llvm::TinyPtrVector<AbstractFunctionDecl *> &vec) {
1725+
// Are all conflicting methods imported from ObjC and in our ObjC half or a
1726+
// bridging header? Some code bases implement ObjC methods in Swift even
1727+
// though it's not exactly supported.
1728+
auto newDeclModuleName = newDecl->getModuleContext()->getName();
1729+
if (llvm::all_of(vec, [&](AbstractFunctionDecl *oldDecl) {
1730+
if (!oldDecl->hasClangNode())
1731+
return false;
1732+
auto oldDeclModuleName = oldDecl->getModuleContext()->getName();
1733+
return oldDeclModuleName == newDeclModuleName
1734+
|| oldDeclModuleName.str() == CLANG_HEADER_MODULE_NAME;
1735+
}))
1736+
return false;
1737+
1738+
// If we're looking at protocol requirements, is the new method an async
1739+
// alternative of any existing method, or vice versa?
1740+
if (isa<ProtocolDecl>(ty) &&
1741+
llvm::any_of(vec, [&](AbstractFunctionDecl *oldDecl) {
17241742
return newDecl->getAsyncAlternative(/*isKnownObjC=*/true) == oldDecl
17251743
|| oldDecl->getAsyncAlternative(/*isKnownObjC=*/true) == newDecl;
1726-
});
1744+
}))
1745+
return false;
1746+
1747+
return true;
17271748
}
17281749

17291750
void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
@@ -1743,7 +1764,7 @@ void NominalTypeDecl::recordObjCMethod(AbstractFunctionDecl *method,
17431764
if (auto *sf = method->getParentSourceFile()) {
17441765
if (vec.empty()) {
17451766
sf->ObjCMethodList.push_back(method);
1746-
} else if (!isa<ProtocolDecl>(this) || !isAnAsyncAlternative(method, vec)) {
1767+
} else if (shouldDiagnoseConflict(this, method, vec)) {
17471768
// We have a conflict.
17481769
sf->ObjCMethodConflicts.insert({ this, selector, isInstanceMethod });
17491770
}

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2269,7 +2269,7 @@ namespace {
22692269
// location within that source file.
22702270
SourceFile *lhsSF = lhs->getDeclContext()->getParentSourceFile();
22712271
SourceFile *rhsSF = rhs->getDeclContext()->getParentSourceFile();
2272-
if (lhsSF == rhsSF) {
2272+
if (lhsSF && lhsSF == rhsSF) {
22732273
// If only one location is valid, the valid location comes first.
22742274
if (lhs->getLoc().isValid() != rhs->getLoc().isValid()) {
22752275
return lhs->getLoc().isValid();

test/ClangImporter/Inputs/custom-modules/module.map

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,8 @@ module CommonName {
271271
header "CommonName.h"
272272
export *
273273
}
274+
275+
module objc_init_redundant {
276+
header "objc_init_redundant.h"
277+
export *
278+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation.h>
2+
3+
@interface MyObject : NSObject
4+
5+
- (void)implementedInSwift;
6+
7+
@end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation.h>
2+
3+
@interface MyBridgedObject : NSObject
4+
5+
- (void)implementedInSwift;
6+
7+
@end

test/ClangImporter/objc_init_redundant.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk -I %S/Inputs/custom-modules) -emit-sil %s -verify
2-
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk -I %S/Inputs/custom-modules) -emit-sil %s > %t.log 2>&1
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk -I %S/Inputs/custom-modules) -import-underlying-module -import-objc-header %S/Inputs/objc_init_redundant_bridging.h -emit-sil %s -verify
2+
// RUN: not %target-swift-frontend(mock-sdk: %clang-importer-sdk -I %S/Inputs/custom-modules) -import-underlying-module -import-objc-header %S/Inputs/objc_init_redundant_bridging.h -emit-sil %s > %t.log 2>&1
33
// RUN: %FileCheck %s < %t.log
44

55
// REQUIRES: objc_interop
@@ -19,3 +19,12 @@ extension NSObject {
1919
// CHECK: ObjectiveC.NSObjectProtocol:{{.*}}note: method 'class()' declared here
2020
}
2121

22+
// rdar://96470068 - Don't want conflict diagnostics in the same module
23+
extension MyObject {
24+
@objc func implementedInSwift() {}
25+
}
26+
27+
// ...or the bridging header
28+
extension MyBridgedObject {
29+
@objc func implementedInSwift() {}
30+
}

0 commit comments

Comments
 (0)