Skip to content

Commit 3cd115e

Browse files
committed
[Concurrency] Implement support for swift_async attribute.
The `swift_async` attribute helps control how a method is imported as `async` into Swift. Implements rdar://70111787.
1 parent abe0308 commit 3cd115e

File tree

5 files changed

+99
-37
lines changed

5 files changed

+99
-37
lines changed

lib/ClangImporter/ImportName.cpp

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1041,7 +1041,26 @@ bool NameImporter::hasNamingConflict(const clang::NamedDecl *decl,
10411041

10421042
static bool shouldBeSwiftPrivate(NameImporter &nameImporter,
10431043
const clang::NamedDecl *decl,
1044-
ImportNameVersion version) {
1044+
ImportNameVersion version,
1045+
bool isAsyncImport) {
1046+
// For an async import, check whether there is a swift_async attribute
1047+
// that specifies whether this should be considered swift_private or not.
1048+
if (isAsyncImport) {
1049+
if (auto *asyncAttr = decl->getAttr<clang::SwiftAsyncAttr>()) {
1050+
switch (asyncAttr->getKind()) {
1051+
case clang::SwiftAsyncAttr::None:
1052+
// Fall through to let us decide based on swift_private.
1053+
break;
1054+
1055+
case clang::SwiftAsyncAttr::SwiftPrivate:
1056+
return true;
1057+
1058+
case clang::SwiftAsyncAttr::NotSwiftPrivate:
1059+
return false;
1060+
}
1061+
}
1062+
}
1063+
10451064
// Decl with the attribute are obviously private
10461065
if (decl->hasAttr<clang::SwiftPrivateAttr>())
10471066
return true;
@@ -1209,7 +1228,9 @@ NameImporter::considerAsyncImport(
12091228
StringRef baseName,
12101229
SmallVectorImpl<StringRef> &paramNames,
12111230
ArrayRef<const clang::ParmVarDecl *> params,
1212-
bool isInitializer, CustomAsyncName customName,
1231+
bool isInitializer,
1232+
Optional<unsigned> explicitCompletionHandlerParamIndex,
1233+
CustomAsyncName customName,
12131234
Optional<ForeignErrorConvention::Info> errorInfo) {
12141235
// If there are no unclaimed parameters, there's no .
12151236
unsigned errorParamAdjust = errorInfo ? 1 : 0;
@@ -1228,43 +1249,50 @@ NameImporter::considerAsyncImport(
12281249
paramNames.size() + errorParamAdjust + customAsyncNameAdjust)
12291250
return None;
12301251

1231-
// The last parameter will be the completion handler for an async function.
1232-
unsigned completionHandlerParamIndex = params.size() - 1;
1233-
unsigned completionHandlerParamNameIndex = paramNames.size() - 1;
1234-
1235-
// Determine whether the naming indicates that this is a completion
1236-
// handler.
1237-
switch (customName) {
1238-
case CustomAsyncName::None:
1239-
// Check whether the first parameter is the completion handler and the
1240-
// base name has a suitable completion-handler suffix.
1241-
if (completionHandlerParamIndex == 0 &&
1242-
stripWithCompletionHandlerSuffix(baseName))
1243-
break;
1252+
// If we don't already know the completion handler parameter index, go
1253+
// try to figure it out.
1254+
unsigned completionHandlerParamIndex;
1255+
unsigned completionHandlerParamNameIndex;
1256+
if (!explicitCompletionHandlerParamIndex) {
1257+
// Determine whether the naming indicates that this is a completion
1258+
// handler.
1259+
completionHandlerParamIndex = params.size() - 1;
1260+
completionHandlerParamNameIndex = paramNames.size() - 1;
1261+
switch (customName) {
1262+
case CustomAsyncName::None:
1263+
// Check whether the first parameter is the completion handler and the
1264+
// base name has a suitable completion-handler suffix.
1265+
if (completionHandlerParamIndex == 0 &&
1266+
stripWithCompletionHandlerSuffix(baseName))
1267+
break;
12441268

1245-
LLVM_FALLTHROUGH;
1269+
LLVM_FALLTHROUGH;
12461270

1247-
case CustomAsyncName::SwiftName:
1248-
// Check whether the argument label itself has an appropriate name.
1249-
if (isCompletionHandlerParamName(
1250-
paramNames[completionHandlerParamNameIndex]) ||
1251-
(completionHandlerParamNameIndex > 0 &&
1252-
stripWithCompletionHandlerSuffix(
1253-
paramNames[completionHandlerParamNameIndex]))) {
1254-
break;
1255-
}
1271+
case CustomAsyncName::SwiftName:
1272+
// Check whether the argument label itself has an appropriate name.
1273+
if (isCompletionHandlerParamName(
1274+
paramNames[completionHandlerParamNameIndex]) ||
1275+
(completionHandlerParamNameIndex > 0 &&
1276+
stripWithCompletionHandlerSuffix(
1277+
paramNames[completionHandlerParamNameIndex]))) {
1278+
break;
1279+
}
12561280

1257-
// Check whether the parameter itself has a name that indicates that
1258-
// it is a completion handelr.
1259-
if (isCompletionHandlerParamName(
1260-
params[completionHandlerParamIndex]->getName()))
1261-
break;
1281+
// Check whether the parameter itself has a name that indicates that
1282+
// it is a completion handelr.
1283+
if (isCompletionHandlerParamName(
1284+
params[completionHandlerParamIndex]->getName()))
1285+
break;
12621286

1263-
return None;
1287+
return None;
12641288

1265-
case CustomAsyncName::SwiftAsyncName:
1266-
// Having a custom async name implies that this is a completion handler.
1267-
break;
1289+
case CustomAsyncName::SwiftAsyncName:
1290+
// Having a custom async name implies that this is a completion handler.
1291+
break;
1292+
}
1293+
} else {
1294+
completionHandlerParamIndex = *explicitCompletionHandlerParamIndex;
1295+
completionHandlerParamNameIndex = *explicitCompletionHandlerParamIndex;
12681296
}
12691297

12701298
// Used for returns once we've determined that the method cannot be
@@ -1448,6 +1476,20 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
14481476
return ImportedName();
14491477
result.effectiveContext = effectiveCtx;
14501478

1479+
// Gather information from the swift_async attribute, if there is one.
1480+
Optional<unsigned> completionHandlerParamIndex;
1481+
if (version.supportsConcurrency()) {
1482+
if (const auto *swiftAsyncAttr = D->getAttr<clang::SwiftAsyncAttr>()) {
1483+
// If this is swift_async(none), don't import as async at all.
1484+
if (swiftAsyncAttr->getKind() == clang::SwiftAsyncAttr::None)
1485+
return ImportedName();
1486+
1487+
// Get the completion handler parameter index, if there is one.
1488+
completionHandlerParamIndex =
1489+
swiftAsyncAttr->getCompletionHandlerIndex().getASTIndex();
1490+
}
1491+
}
1492+
14511493
// FIXME: ugly to check here, instead perform unified check up front in
14521494
// containing struct...
14531495
if (findSwiftNewtype(D, clangSema, version))
@@ -1597,6 +1639,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15971639
if (auto asyncInfo = considerAsyncImport(
15981640
method, parsedName.BaseName, parsedName.ArgumentLabels,
15991641
params, isInitializer,
1642+
completionHandlerParamIndex,
16001643
nameAttr->isAsync ? CustomAsyncName::SwiftAsyncName
16011644
: CustomAsyncName::SwiftName,
16021645
result.getErrorInfo())) {
@@ -1886,7 +1929,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
18861929
result.info.accessorKind == ImportedAccessorKind::None) {
18871930
if (auto asyncInfo = considerAsyncImport(
18881931
objcMethod, baseName, argumentNames, params, isInitializer,
1889-
CustomAsyncName::None, result.getErrorInfo())) {
1932+
completionHandlerParamIndex, CustomAsyncName::None,
1933+
result.getErrorInfo())) {
18901934
result.info.hasAsyncInfo = true;
18911935
result.info.asyncInfo = *asyncInfo;
18921936
}
@@ -2061,7 +2105,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
20612105
// If this declaration has the swift_private attribute, prepend "__" to the
20622106
// appropriate place.
20632107
SmallString<16> swiftPrivateScratch;
2064-
if (shouldBeSwiftPrivate(*this, D, version)) {
2108+
if (shouldBeSwiftPrivate(*this, D, version, result.info.hasAsyncInfo)) {
20652109
// Special case: empty arg factory, "for historical reasons", is not private
20662110
if (isInitializer && argumentNames.empty() &&
20672111
(result.getInitKind() == CtorInitializerKind::Factory ||

lib/ClangImporter/ImportName.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,9 @@ class NameImporter {
469469
StringRef baseName,
470470
SmallVectorImpl<StringRef> &paramNames,
471471
ArrayRef<const clang::ParmVarDecl *> params,
472-
bool isInitializer, CustomAsyncName customName,
472+
bool isInitializer,
473+
Optional<unsigned> explicitCompletionHandlerParamIndex,
474+
CustomAsyncName customName,
473475
Optional<ForeignErrorConvention::Info> errorInfo);
474476

475477
EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *,

test/ClangImporter/objc_async.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,22 @@ func testSlowServer(slowServer: SlowServer) async throws {
3030

3131
let _: Int = await slowServer.bestName("hello")
3232
let _: Int = await slowServer.customize("hello")
33+
34+
let _: String = await slowServer.dance("slide")
35+
let _: String = await slowServer.__leap(17)
36+
37+
slowServer.repeatTrick("jump") // expected-error{{missing argument for parameter 'completionHandler' in call}}
3338
}
3439

3540
func testSlowServerSynchronous(slowServer: SlowServer) {
3641
// synchronous version
3742
let _: Int = slowServer.doSomethingConflicted("thinking")
3843
slowServer.poorlyNamed("hello") { (i: Int) in print(i) }
3944
slowServer.customize(with: "hello") { (i: Int) in print(i) }
45+
46+
slowServer.dance("jig") { s in print(s + "") }
47+
slowServer.leap(17) { s in print(s + "") }
48+
slowServer.repeatTrick("jump") { i in print(i + 1) }
4049
}
4150

4251
func testSlowServerOldSchool(slowServer: SlowServer) {

test/IDE/print_clang_objc_async.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
// CHECK-DAG: func findAnswerFailingly(completionHandler handler: @escaping (String?, Error?) -> Void) throws
1919
// CHECK-DAG: func findAnswerFailingly() async throws -> String
2020
// CHECK-DAG: func doSomethingFun(_ operation: String) async
21+
// CHECK-DAG: func dance(_ step: String) async -> String
22+
// CHECK-DAG: func __leap(_ height: Int) async -> String
2123
// CHECK: {{^[}]$}}
2224

2325
// CHECK-LABEL: protocol RefrigeratorDelegate

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@
3232

3333
-(void)customizedWithString:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_name("customize(with:completionHandler:)"))) __attribute__((swift_async_name("customize(_:)")));
3434

35+
-(void)dance:(NSString *)step andThen:(void (^)(NSString *))doSomething __attribute__((swift_async(not_swift_private,2)));
36+
-(void)leap:(NSInteger)height andThen:(void (^)(NSString *))doSomething __attribute__((swift_async(swift_private,2)));
37+
38+
-(void)repeatTrick:(NSString *)trick completionHandler:(void (^)(NSInteger))handler __attribute__((swift_async(none)));
39+
3540
@end
3641

3742
@protocol RefrigeratorDelegate<NSObject>

0 commit comments

Comments
 (0)