Skip to content

Commit fe4a8bb

Browse files
committed
[Clang importer] Allow both sync and async imports with the same name.
The Clang importer was filtering out cases where the same declaration is imported twice under the same name, which can now happen when one is synchronous and one is asynchronous. This happens when, e.g., an Objective-C class provides both a completion-hander-based asynchronous version and a synchronous version, and the Swift names line up after the completion-handler parameter is dropped. Stop filtering these out. Overload resolution is capable of handling synchronous/asynchronous overloading based on context.
1 parent e36011d commit fe4a8bb

File tree

4 files changed

+72
-47
lines changed

4 files changed

+72
-47
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3701,48 +3701,46 @@ void ClangImporter::Implementation::lookupValue(
37013701

37023702
// If we have a declaration and nothing matched so far, try the names used
37033703
// in other versions of Swift.
3704-
if (!anyMatching) {
3705-
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
3706-
const clang::NamedDecl *recentClangDecl =
3707-
clangDecl->getMostRecentDecl();
3708-
3709-
CurrentVersion.forEachOtherImportNameVersion(
3710-
SwiftContext.LangOpts.EnableExperimentalConcurrency,
3711-
[&](ImportNameVersion nameVersion) {
3712-
if (anyMatching)
3713-
return;
3714-
3715-
// Check to see if the name and context match what we expect.
3716-
ImportedName newName = importFullName(recentClangDecl, nameVersion);
3717-
if (!newName.getDeclName().matchesRef(name))
3718-
return;
3719-
3720-
// If we asked for an async import and didn't find one, skip this.
3721-
// This filters out duplicates.
3722-
if (nameVersion.supportsConcurrency() &&
3723-
!newName.getAsyncInfo())
3724-
return;
3725-
3726-
const clang::DeclContext *clangDC =
3727-
newName.getEffectiveContext().getAsDeclContext();
3728-
if (!clangDC || !clangDC->isFileContext())
3729-
return;
3730-
3731-
// Then try to import the decl under the alternate name.
3732-
auto alternateNamedDecl =
3733-
cast_or_null<ValueDecl>(importDeclReal(recentClangDecl,
3734-
nameVersion));
3735-
if (!alternateNamedDecl || alternateNamedDecl == decl)
3736-
return;
3737-
assert(alternateNamedDecl->getName().matchesRef(name) &&
3738-
"importFullName behaved differently from importDecl");
3739-
if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) {
3740-
consumer.foundDecl(alternateNamedDecl,
3741-
DeclVisibilityKind::VisibleAtTopLevel);
3742-
anyMatching = true;
3743-
}
3744-
});
3745-
}
3704+
if (auto clangDecl = entry.dyn_cast<clang::NamedDecl *>()) {
3705+
const clang::NamedDecl *recentClangDecl =
3706+
clangDecl->getMostRecentDecl();
3707+
3708+
CurrentVersion.forEachOtherImportNameVersion(
3709+
SwiftContext.LangOpts.EnableExperimentalConcurrency,
3710+
[&](ImportNameVersion nameVersion) {
3711+
if (anyMatching)
3712+
return;
3713+
3714+
// Check to see if the name and context match what we expect.
3715+
ImportedName newName = importFullName(recentClangDecl, nameVersion);
3716+
if (!newName.getDeclName().matchesRef(name))
3717+
return;
3718+
3719+
// If we asked for an async import and didn't find one, skip this.
3720+
// This filters out duplicates.
3721+
if (nameVersion.supportsConcurrency() &&
3722+
!newName.getAsyncInfo())
3723+
return;
3724+
3725+
const clang::DeclContext *clangDC =
3726+
newName.getEffectiveContext().getAsDeclContext();
3727+
if (!clangDC || !clangDC->isFileContext())
3728+
return;
3729+
3730+
// Then try to import the decl under the alternate name.
3731+
auto alternateNamedDecl =
3732+
cast_or_null<ValueDecl>(importDeclReal(recentClangDecl,
3733+
nameVersion));
3734+
if (!alternateNamedDecl || alternateNamedDecl == decl)
3735+
return;
3736+
assert(alternateNamedDecl->getName().matchesRef(name) &&
3737+
"importFullName behaved differently from importDecl");
3738+
if (alternateNamedDecl->getDeclContext()->isModuleScopeContext()) {
3739+
consumer.foundDecl(alternateNamedDecl,
3740+
DeclVisibilityKind::VisibleAtTopLevel);
3741+
anyMatching = true;
3742+
}
3743+
});
37463744
}
37473745
}
37483746
}

lib/ClangImporter/ImportName.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2084,13 +2084,15 @@ ImportedName NameImporter::importName(const clang::NamedDecl *decl,
20842084
bool NameImporter::forEachDistinctImportName(
20852085
const clang::NamedDecl *decl, ImportNameVersion activeVersion,
20862086
llvm::function_ref<bool(ImportedName, ImportNameVersion)> action) {
2087-
using ImportNameKey = std::pair<DeclName, EffectiveClangContext>;
2087+
using ImportNameKey = std::tuple<DeclName, EffectiveClangContext, bool>;
20882088
SmallVector<ImportNameKey, 8> seenNames;
20892089

20902090
ImportedName newName = importName(decl, activeVersion);
20912091
if (!newName)
20922092
return true;
2093-
ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext());
2093+
2094+
ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext(),
2095+
newName.getAsyncInfo().hasValue());
20942096
if (action(newName, activeVersion))
20952097
seenNames.push_back(key);
20962098

@@ -2101,15 +2103,18 @@ bool NameImporter::forEachDistinctImportName(
21012103
ImportedName newName = importName(decl, nameVersion);
21022104
if (!newName)
21032105
return;
2104-
ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext());
2106+
ImportNameKey key(newName.getDeclName(), newName.getEffectiveContext(),
2107+
newName.getAsyncInfo().hasValue());
21052108

21062109
bool seen = llvm::any_of(
21072110
seenNames, [&key](const ImportNameKey &existing) -> bool {
2108-
return key.first == existing.first &&
2109-
key.second.equalsWithoutResolving(existing.second);
2111+
return std::get<0>(key) == std::get<0>(existing) &&
2112+
std::get<2>(key) == std::get<2>(existing) &&
2113+
std::get<1>(key).equalsWithoutResolving(std::get<1>(existing));
21102114
});
21112115
if (seen)
21122116
return;
2117+
21132118
if (action(newName, nameVersion))
21142119
seenNames.push_back(key);
21152120
});

test/ClangImporter/objc_async.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ func testSlowServer(slowServer: SlowServer) async throws {
1111
let _: String = await try slowServer.findAnswerFailingly() ?? "nope"
1212
let _: Void = await slowServer.doSomethingFun("jump")
1313
let _: (Int) -> Void = slowServer.completionHandler
14+
15+
// async version
16+
let _: Int = await slowServer.doSomethingConflicted("thinking")
17+
18+
// still async version...
19+
let _: Int = slowServer.doSomethingConflicted("thinking")
20+
// expected-error@-1{{call is 'async' but is not marked with 'await'}}
21+
}
22+
23+
func testSlowServerSynchronous(slowServer: SlowServer) {
24+
// synchronous version
25+
let _: Int = slowServer.doSomethingConflicted("thinking")
1426
}
1527

1628
func testSlowServerOldSchool(slowServer: SlowServer) {

test/Inputs/clang-importer-sdk/usr/include/ObjCConcurrency.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
-(BOOL)findAnswerFailinglyWithError:(NSError * _Nullable * _Nullable)error completion:(void (^)(NSString *_Nullable, NSError * _Nullable))handler __attribute__((swift_name("findAnswerFailingly(completionHandler:)")));
1212
-(void)doSomethingFun:(NSString *)operation then:(void (^)(void))completionHandler;
1313
@property(readwrite) void (^completionHandler)(NSInteger);
14+
15+
-(void)doSomethingConflicted:(NSString *)operation completionHandler:(void (^)(NSInteger))handler;
16+
-(NSInteger)doSomethingConflicted:(NSString *)operation;
1417
@end
1518

1619
@protocol RefrigeratorDelegate<NSObject>
@@ -21,4 +24,11 @@
2124
- (BOOL)refrigerator:(id)fridge didRemoveItem:(id)item;
2225
@end
2326

27+
@protocol ConcurrentProtocol
28+
-(void)askUserToSolvePuzzle:(NSString *)puzzle completionHandler:(void (^ _Nullable)(NSString * _Nullable, NSError * _Nullable))completionHandler;
29+
30+
@optional
31+
-(void)askUserToJumpThroughHoop:(NSString *)hoop completionHandler:(void (^ _Nullable)(NSString *))completionHandler;
32+
@end
33+
2434
#pragma clang assume_nonnull end

0 commit comments

Comments
 (0)