Skip to content

Commit c55b9cc

Browse files
authored
Merge pull request swiftlang#34984 from DougGregor/import-swift-async-name
[Concurrency] Adopt Clang swift_async_name attribute.
2 parents 89dba54 + abe0308 commit c55b9cc

File tree

4 files changed

+137
-42
lines changed

4 files changed

+137
-42
lines changed

lib/ClangImporter/ImportName.cpp

Lines changed: 115 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -539,12 +539,26 @@ determineFactoryInitializerKind(const clang::ObjCMethodDecl *method) {
539539
}
540540

541541
namespace {
542+
/// Describes the details of any swift_name or swift_async_name
543+
/// attribute found via
544+
struct AnySwiftNameAttr {
545+
/// The name itself.
546+
StringRef name;
547+
548+
/// Whether this was a swift_async_name attribute.
549+
bool isAsync;
550+
551+
friend bool operator==(AnySwiftNameAttr lhs, AnySwiftNameAttr rhs) {
552+
return lhs.name == rhs.name && lhs.isAsync == rhs.isAsync;
553+
}
554+
};
555+
542556
/// Aggregate struct for the common members of clang::SwiftVersionedAttr and
543557
/// clang::SwiftVersionedRemovalAttr.
544558
///
545559
/// For a SwiftVersionedRemovalAttr, the Attr member will be null.
546560
struct VersionedSwiftNameInfo {
547-
const clang::SwiftNameAttr *Attr;
561+
Optional<AnySwiftNameAttr> Attr;
548562
llvm::VersionTuple Version;
549563
bool IsReplacedByActive;
550564
};
@@ -594,8 +608,7 @@ checkVersionedSwiftName(VersionedSwiftNameInfo info,
594608
return VersionedSwiftNameAction::Use;
595609
}
596610

597-
598-
static const clang::SwiftNameAttr *
611+
static Optional<AnySwiftNameAttr>
599612
findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
600613
#ifndef NDEBUG
601614
if (Optional<const clang::Decl *> def = getDefinitionForClangTypeDecl(decl)) {
@@ -605,7 +618,24 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
605618
#endif
606619

607620
if (version == ImportNameVersion::raw())
608-
return nullptr;
621+
return None;
622+
623+
/// Decode the given Clang attribute to try to determine whether it is
624+
/// a Swift name attribute.
625+
auto decodeAttr =
626+
[&](const clang::Attr *attr) -> Optional<AnySwiftNameAttr> {
627+
if (version.supportsConcurrency()) {
628+
if (auto asyncAttr = dyn_cast<clang::SwiftAsyncNameAttr>(attr)) {
629+
return AnySwiftNameAttr { asyncAttr->getName(), /*isAsync=*/true };
630+
}
631+
}
632+
633+
if (auto nameAttr = dyn_cast<clang::SwiftNameAttr>(attr)) {
634+
return AnySwiftNameAttr { nameAttr->getName(), /*isAsync=*/false };
635+
}
636+
637+
return None;
638+
};
609639

610640
// Handle versioned API notes for Swift 3 and later. This is the common case.
611641
if (version > ImportNameVersion::swift2()) {
@@ -615,15 +645,22 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
615645
if (importer::isSpecialUIKitStructZeroProperty(namedDecl))
616646
version = ImportNameVersion::swift4_2();
617647

618-
const auto *activeAttr = decl->getAttr<clang::SwiftNameAttr>();
619-
const clang::SwiftNameAttr *result = activeAttr;
648+
// Dig out the attribute that specifies the Swift name.
649+
Optional<AnySwiftNameAttr> activeAttr;
650+
if (auto asyncAttr = decl->getAttr<clang::SwiftAsyncNameAttr>())
651+
activeAttr = decodeAttr(asyncAttr);
652+
if (!activeAttr) {
653+
if (auto nameAttr = decl->getAttr<clang::SwiftNameAttr>())
654+
activeAttr = decodeAttr(nameAttr);
655+
}
656+
657+
Optional<AnySwiftNameAttr> result = activeAttr;
620658
llvm::VersionTuple bestSoFar;
621659
for (auto *attr : decl->attrs()) {
622660
VersionedSwiftNameInfo info;
623661

624662
if (auto *versionedAttr = dyn_cast<clang::SwiftVersionedAttr>(attr)) {
625-
auto *added =
626-
dyn_cast<clang::SwiftNameAttr>(versionedAttr->getAttrToAdd());
663+
auto added = decodeAttr(versionedAttr->getAttrToAdd());
627664
if (!added)
628665
continue;
629666

@@ -634,7 +671,7 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
634671
dyn_cast<clang::SwiftVersionedRemovalAttr>(attr)) {
635672
if (removeAttr->getAttrKindToRemove() != clang::attr::SwiftName)
636673
continue;
637-
info = {nullptr, removeAttr->getVersion(),
674+
info = {None, removeAttr->getVersion(),
638675
removeAttr->getIsReplacedByActive()};
639676

640677
} else {
@@ -673,11 +710,11 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
673710
// The remainder of this function emulates the limited form of swift_name
674711
// supported in Swift 2.
675712
auto attr = decl->getAttr<clang::SwiftNameAttr>();
676-
if (!attr) return nullptr;
713+
if (!attr) return None;
677714

678715
// API notes produce attributes with no source location; ignore them because
679716
// they weren't used for naming in Swift 2.
680-
if (attr->getLocation().isInvalid()) return nullptr;
717+
if (attr->getLocation().isInvalid()) return None;
681718

682719
// Hardcode certain kinds of explicitly-written Swift names that were
683720
// permitted and used in Swift 2. All others are ignored, so that we are
@@ -686,28 +723,28 @@ findSwiftNameAttr(const clang::Decl *decl, ImportNameVersion version) {
686723
if (auto enumerator = dyn_cast<clang::EnumConstantDecl>(decl)) {
687724
// Foundation's NSXMLDTDKind had an explicit swift_name attribute in
688725
// Swift 2. Honor it.
689-
if (enumerator->getName() == "NSXMLDTDKind") return attr;
690-
return nullptr;
726+
if (enumerator->getName() == "NSXMLDTDKind") return decodeAttr(attr);
727+
return None;
691728
}
692729

693730
if (auto method = dyn_cast<clang::ObjCMethodDecl>(decl)) {
694731
// Special case: mapping to an initializer.
695732
if (attr->getName().startswith("init(")) {
696733
// If we have a class method, honor the annotation to turn a class
697734
// method into an initializer.
698-
if (method->isClassMethod()) return attr;
735+
if (method->isClassMethod()) return decodeAttr(attr);
699736

700-
return nullptr;
737+
return None;
701738
}
702739

703740
// Special case: preventing a mapping to an initializer.
704741
if (matchFactoryAsInitName(method) && determineFactoryInitializerKind(method))
705-
return attr;
742+
return decodeAttr(attr);
706743

707-
return nullptr;
744+
return None;
708745
}
709746

710-
return nullptr;
747+
return None;
711748
}
712749

713750
/// Determine whether the given class method should be imported as
@@ -716,8 +753,8 @@ static FactoryAsInitKind
716753
getFactoryAsInit(const clang::ObjCInterfaceDecl *classDecl,
717754
const clang::ObjCMethodDecl *method,
718755
ImportNameVersion version) {
719-
if (auto *customNameAttr = findSwiftNameAttr(method, version)) {
720-
if (customNameAttr->getName().startswith("init("))
756+
if (auto customNameAttr = findSwiftNameAttr(method, version)) {
757+
if (customNameAttr->name.startswith("init("))
721758
return FactoryAsInitKind::AsInitializer;
722759
else
723760
return FactoryAsInitKind::AsClassMethod;
@@ -1176,17 +1213,23 @@ NameImporter::considerAsyncImport(
11761213
StringRef baseName,
11771214
SmallVectorImpl<StringRef> &paramNames,
11781215
ArrayRef<const clang::ParmVarDecl *> params,
1179-
bool isInitializer, bool hasCustomName,
1216+
bool isInitializer, CustomAsyncName customName,
11801217
Optional<ForeignErrorConvention::Info> errorInfo) {
11811218
// If there are no unclaimed parameters, there's no .
11821219
unsigned errorParamAdjust = errorInfo ? 1 : 0;
11831220
if (params.size() - errorParamAdjust == 0)
11841221
return None;
11851222

1223+
// When there is a custom async name, it will have removed the completion
1224+
// handler parameter already.
1225+
unsigned customAsyncNameAdjust =
1226+
customName == CustomAsyncName::SwiftAsyncName ? 1 : 0;
1227+
11861228
// If the # of parameter names doesn't line up with the # of parameters,
11871229
// bail out. There are extra C parameters on the method or a custom name
11881230
// was incorrect.
1189-
if (params.size() != paramNames.size() + errorParamAdjust)
1231+
if (params.size() !=
1232+
paramNames.size() + errorParamAdjust + customAsyncNameAdjust)
11901233
return None;
11911234

11921235
// The last parameter will be the completion handler for an async function.
@@ -1195,20 +1238,37 @@ NameImporter::considerAsyncImport(
11951238

11961239
// Determine whether the naming indicates that this is a completion
11971240
// handler.
1198-
if (isCompletionHandlerParamName(
1199-
paramNames[completionHandlerParamNameIndex]) ||
1200-
(completionHandlerParamNameIndex > 0 &&
1201-
stripWithCompletionHandlerSuffix(
1202-
paramNames[completionHandlerParamNameIndex]))) {
1203-
// The argument label itself has an appropriate name.
1204-
} else if (!hasCustomName && completionHandlerParamIndex == 0 &&
1205-
stripWithCompletionHandlerSuffix(baseName)) {
1206-
// The base name implies that the first parameter is a completion handler.
1207-
} else if (isCompletionHandlerParamName(
1208-
params[completionHandlerParamIndex]->getName())) {
1209-
// The parameter has an appropriate name.
1210-
} else {
1241+
switch (customName) {
1242+
case CustomAsyncName::None:
1243+
// Check whether the first parameter is the completion handler and the
1244+
// base name has a suitable completion-handler suffix.
1245+
if (completionHandlerParamIndex == 0 &&
1246+
stripWithCompletionHandlerSuffix(baseName))
1247+
break;
1248+
1249+
LLVM_FALLTHROUGH;
1250+
1251+
case CustomAsyncName::SwiftName:
1252+
// Check whether the argument label itself has an appropriate name.
1253+
if (isCompletionHandlerParamName(
1254+
paramNames[completionHandlerParamNameIndex]) ||
1255+
(completionHandlerParamNameIndex > 0 &&
1256+
stripWithCompletionHandlerSuffix(
1257+
paramNames[completionHandlerParamNameIndex]))) {
1258+
break;
1259+
}
1260+
1261+
// Check whether the parameter itself has a name that indicates that
1262+
// it is a completion handelr.
1263+
if (isCompletionHandlerParamName(
1264+
params[completionHandlerParamIndex]->getName()))
1265+
break;
1266+
12111267
return None;
1268+
1269+
case CustomAsyncName::SwiftAsyncName:
1270+
// Having a custom async name implies that this is a completion handler.
1271+
break;
12121272
}
12131273

12141274
// Used for returns once we've determined that the method cannot be
@@ -1288,8 +1348,16 @@ NameImporter::considerAsyncImport(
12881348
break;
12891349
}
12901350

1291-
// Drop the completion handler parameter name.
1292-
paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex);
1351+
// Drop the completion handler parameter name when needed.
1352+
switch (customName) {
1353+
case CustomAsyncName::None:
1354+
case CustomAsyncName::SwiftName:
1355+
paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex);
1356+
break;
1357+
1358+
case CustomAsyncName::SwiftAsyncName:
1359+
break;
1360+
}
12931361

12941362
return ForeignAsyncConvention::Info(
12951363
completionHandlerParamIndex, completionHandlerErrorParamIndex);
@@ -1453,11 +1521,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
14531521
}
14541522

14551523
// If we have a swift_name attribute, use that.
1456-
if (auto *nameAttr = findSwiftNameAttr(D, version)) {
1524+
if (auto nameAttr = findSwiftNameAttr(D, version)) {
14571525
bool skipCustomName = false;
14581526

14591527
// Parse the name.
1460-
ParsedDeclName parsedName = parseDeclName(nameAttr->getName());
1528+
ParsedDeclName parsedName = parseDeclName(nameAttr->name);
14611529
if (!parsedName || parsedName.isOperator())
14621530
return result;
14631531

@@ -1532,7 +1600,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15321600
if (version.supportsConcurrency()) {
15331601
if (auto asyncInfo = considerAsyncImport(
15341602
method, parsedName.BaseName, parsedName.ArgumentLabels,
1535-
params, isInitializer, /*hasCustomName=*/true,
1603+
params, isInitializer,
1604+
nameAttr->isAsync ? CustomAsyncName::SwiftAsyncName
1605+
: CustomAsyncName::SwiftName,
15361606
result.getErrorInfo())) {
15371607
result.info.hasAsyncInfo = true;
15381608
result.info.asyncInfo = *asyncInfo;
@@ -1541,6 +1611,10 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15411611
result.declName = formDeclName(
15421612
swiftCtx, parsedName.BaseName, parsedName.ArgumentLabels,
15431613
/*isFunction=*/true, isInitializer);
1614+
} else if (nameAttr->isAsync) {
1615+
// The custom name was for an async import, but we didn't in fact
1616+
// import as async for some reason. Ignore this import.
1617+
return ImportedName();
15441618
}
15451619
}
15461620
}
@@ -1816,7 +1890,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
18161890
result.info.accessorKind == ImportedAccessorKind::None) {
18171891
if (auto asyncInfo = considerAsyncImport(
18181892
objcMethod, baseName, argumentNames, params, isInitializer,
1819-
/*hasCustomName=*/false, result.getErrorInfo())) {
1893+
CustomAsyncName::None, result.getErrorInfo())) {
18201894
result.info.hasAsyncInfo = true;
18211895
result.info.asyncInfo = *asyncInfo;
18221896
}

lib/ClangImporter/ImportName.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,17 @@ class ImportedName {
335335
/// in "Notification", or it there would be nothing left.
336336
StringRef stripNotification(StringRef name);
337337

338+
/// Describes how a custom name was provided for 'async' import.
339+
enum class CustomAsyncName {
340+
/// No custom name was provided.
341+
None,
342+
/// A custom swift_name (but not swift_async_name) was provided.
343+
SwiftName,
344+
/// A custom swift_async_name was provided, which won't have a completion
345+
/// handler argument label.
346+
SwiftAsyncName,
347+
};
348+
338349
/// Class to determine the Swift name of foreign entities. Currently fairly
339350
/// stateless and borrows from the ClangImporter::Implementation, but in the
340351
/// future will be more self-contained and encapsulated.
@@ -458,7 +469,7 @@ class NameImporter {
458469
StringRef baseName,
459470
SmallVectorImpl<StringRef> &paramNames,
460471
ArrayRef<const clang::ParmVarDecl *> params,
461-
bool isInitializer, bool hasCustomName,
472+
bool isInitializer, CustomAsyncName customName,
462473
Optional<ForeignErrorConvention::Info> errorInfo);
463474

464475
EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *,

test/ClangImporter/objc_async.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,16 @@ func testSlowServer(slowServer: SlowServer) async throws {
3636
await slowServer.serverRestart("localhost", atPriority: 0.8)
3737

3838
_ = await slowServer.allOperations()
39+
40+
let _: Int = await slowServer.bestName("hello")
41+
let _: Int = await slowServer.customize("hello")
3942
}
4043

4144
func testSlowServerSynchronous(slowServer: SlowServer) {
4245
// synchronous version
4346
let _: Int = slowServer.doSomethingConflicted("thinking")
47+
slowServer.poorlyNamed("hello") { (i: Int) in print(i) }
48+
slowServer.customize(with: "hello") { (i: Int) in print(i) }
4449
}
4550

4651
func testSlowServerOldSchool(slowServer: SlowServer) {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ typedef void (^CompletionHandler)(NSString * _Nullable, NSString * _Nullable_res
3434
-(NSInteger)doSomethingConflicted:(NSString *)operation;
3535
-(void)server:(NSString *)name restartWithCompletionHandler:(void (^)(void))block;
3636
-(void)server:(NSString *)name atPriority:(double)priority restartWithCompletionHandler:(void (^)(void))block;
37+
38+
-(void)poorlyNamed:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_async_name("bestName(_:)")));
39+
40+
-(void)customizedWithString:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_name("customize(with:completionHandler:)"))) __attribute__((swift_async_name("customize(_:)")));
41+
3742
@end
3843

3944
@protocol RefrigeratorDelegate<NSObject>

0 commit comments

Comments
 (0)