Skip to content

Commit abe0308

Browse files
committed
[Concurrency] Adopt Clang swift_async_name attribute.
The new `swift_async_name` attribute allows Clang declarations to specify their Swift names only for `async` import, which may differ from those used for importing as a completion handler. Implements rdar://70111787.
1 parent 27ece6f commit abe0308

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;
@@ -1172,17 +1209,23 @@ NameImporter::considerAsyncImport(
11721209
StringRef baseName,
11731210
SmallVectorImpl<StringRef> &paramNames,
11741211
ArrayRef<const clang::ParmVarDecl *> params,
1175-
bool isInitializer, bool hasCustomName,
1212+
bool isInitializer, CustomAsyncName customName,
11761213
Optional<ForeignErrorConvention::Info> errorInfo) {
11771214
// If there are no unclaimed parameters, there's no .
11781215
unsigned errorParamAdjust = errorInfo ? 1 : 0;
11791216
if (params.size() - errorParamAdjust == 0)
11801217
return None;
11811218

1219+
// When there is a custom async name, it will have removed the completion
1220+
// handler parameter already.
1221+
unsigned customAsyncNameAdjust =
1222+
customName == CustomAsyncName::SwiftAsyncName ? 1 : 0;
1223+
11821224
// If the # of parameter names doesn't line up with the # of parameters,
11831225
// bail out. There are extra C parameters on the method or a custom name
11841226
// was incorrect.
1185-
if (params.size() != paramNames.size() + errorParamAdjust)
1227+
if (params.size() !=
1228+
paramNames.size() + errorParamAdjust + customAsyncNameAdjust)
11861229
return None;
11871230

11881231
// The last parameter will be the completion handler for an async function.
@@ -1191,20 +1234,37 @@ NameImporter::considerAsyncImport(
11911234

11921235
// Determine whether the naming indicates that this is a completion
11931236
// handler.
1194-
if (isCompletionHandlerParamName(
1195-
paramNames[completionHandlerParamNameIndex]) ||
1196-
(completionHandlerParamNameIndex > 0 &&
1197-
stripWithCompletionHandlerSuffix(
1198-
paramNames[completionHandlerParamNameIndex]))) {
1199-
// The argument label itself has an appropriate name.
1200-
} else if (!hasCustomName && completionHandlerParamIndex == 0 &&
1201-
stripWithCompletionHandlerSuffix(baseName)) {
1202-
// The base name implies that the first parameter is a completion handler.
1203-
} else if (isCompletionHandlerParamName(
1204-
params[completionHandlerParamIndex]->getName())) {
1205-
// The parameter has an appropriate name.
1206-
} else {
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;
1244+
1245+
LLVM_FALLTHROUGH;
1246+
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+
}
1256+
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;
1262+
12071263
return None;
1264+
1265+
case CustomAsyncName::SwiftAsyncName:
1266+
// Having a custom async name implies that this is a completion handler.
1267+
break;
12081268
}
12091269

12101270
// Used for returns once we've determined that the method cannot be
@@ -1284,8 +1344,16 @@ NameImporter::considerAsyncImport(
12841344
break;
12851345
}
12861346

1287-
// Drop the completion handler parameter name.
1288-
paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex);
1347+
// Drop the completion handler parameter name when needed.
1348+
switch (customName) {
1349+
case CustomAsyncName::None:
1350+
case CustomAsyncName::SwiftName:
1351+
paramNames.erase(paramNames.begin() + completionHandlerParamNameIndex);
1352+
break;
1353+
1354+
case CustomAsyncName::SwiftAsyncName:
1355+
break;
1356+
}
12891357

12901358
return ForeignAsyncConvention::Info(
12911359
completionHandlerParamIndex, completionHandlerErrorParamIndex);
@@ -1449,11 +1517,11 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
14491517
}
14501518

14511519
// If we have a swift_name attribute, use that.
1452-
if (auto *nameAttr = findSwiftNameAttr(D, version)) {
1520+
if (auto nameAttr = findSwiftNameAttr(D, version)) {
14531521
bool skipCustomName = false;
14541522

14551523
// Parse the name.
1456-
ParsedDeclName parsedName = parseDeclName(nameAttr->getName());
1524+
ParsedDeclName parsedName = parseDeclName(nameAttr->name);
14571525
if (!parsedName || parsedName.isOperator())
14581526
return result;
14591527

@@ -1528,7 +1596,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15281596
if (version.supportsConcurrency()) {
15291597
if (auto asyncInfo = considerAsyncImport(
15301598
method, parsedName.BaseName, parsedName.ArgumentLabels,
1531-
params, isInitializer, /*hasCustomName=*/true,
1599+
params, isInitializer,
1600+
nameAttr->isAsync ? CustomAsyncName::SwiftAsyncName
1601+
: CustomAsyncName::SwiftName,
15321602
result.getErrorInfo())) {
15331603
result.info.hasAsyncInfo = true;
15341604
result.info.asyncInfo = *asyncInfo;
@@ -1537,6 +1607,10 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
15371607
result.declName = formDeclName(
15381608
swiftCtx, parsedName.BaseName, parsedName.ArgumentLabels,
15391609
/*isFunction=*/true, isInitializer);
1610+
} else if (nameAttr->isAsync) {
1611+
// The custom name was for an async import, but we didn't in fact
1612+
// import as async for some reason. Ignore this import.
1613+
return ImportedName();
15401614
}
15411615
}
15421616
}
@@ -1812,7 +1886,7 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
18121886
result.info.accessorKind == ImportedAccessorKind::None) {
18131887
if (auto asyncInfo = considerAsyncImport(
18141888
objcMethod, baseName, argumentNames, params, isInitializer,
1815-
/*hasCustomName=*/false, result.getErrorInfo())) {
1889+
CustomAsyncName::None, result.getErrorInfo())) {
18161890
result.info.hasAsyncInfo = true;
18171891
result.info.asyncInfo = *asyncInfo;
18181892
}

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
@@ -27,11 +27,16 @@ func testSlowServer(slowServer: SlowServer) async throws {
2727
await slowServer.server("localhost", atPriorityRestart: 0.8)
2828

2929
_ = await slowServer.allOperations()
30+
31+
let _: Int = await slowServer.bestName("hello")
32+
let _: Int = await slowServer.customize("hello")
3033
}
3134

3235
func testSlowServerSynchronous(slowServer: SlowServer) {
3336
// synchronous version
3437
let _: Int = slowServer.doSomethingConflicted("thinking")
38+
slowServer.poorlyNamed("hello") { (i: Int) in print(i) }
39+
slowServer.customize(with: "hello") { (i: Int) in print(i) }
3540
}
3641

3742
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
@@ -27,6 +27,11 @@
2727
-(NSInteger)doSomethingConflicted:(NSString *)operation;
2828
-(void)server:(NSString *)name restartWithCompletionHandler:(void (^)(void))block;
2929
-(void)server:(NSString *)name atPriority:(double)priority restartWithCompletionHandler:(void (^)(void))block;
30+
31+
-(void)poorlyNamed:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_async_name("bestName(_:)")));
32+
33+
-(void)customizedWithString:(NSString *)operation completionHandler:(void (^)(NSInteger))handler __attribute__((swift_name("customize(with:completionHandler:)"))) __attribute__((swift_async_name("customize(_:)")));
34+
3035
@end
3136

3237
@protocol RefrigeratorDelegate<NSObject>

0 commit comments

Comments
 (0)