Skip to content

Commit 0f18948

Browse files
committed
[Concurrency] Allow @objc async methods.
Allow the declaration of @objc async methods, mapping them to a completion-handler API in Objective-C. This covers most of the checking and semantics within the type checker: * Declaring @objc async methods and checking their parameter/result types * Determining the default Objective-C selector by adding completionHandler/WithCompletionHandler as appropriate * Determining the type of the completion handler parameter * Inferring @objc from protocol requirements * Inferring @objc from an overridden method
1 parent 191a4f8 commit 0f18948

18 files changed

+387
-87
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,8 @@ ERROR(cdecl_empty_name,none,
13841384
"@_cdecl symbol name cannot be empty", ())
13851385
ERROR(cdecl_throws,none,
13861386
"raising errors from @_cdecl functions is not supported", ())
1387+
ERROR(cdecl_async,none,
1388+
"@_cdecl functions cannot be asynchronous", ())
13871389

13881390
ERROR(attr_methods_only,none,
13891391
"only methods can be declared %0", (DeclAttribute))
@@ -4117,6 +4119,17 @@ ERROR(asynchandler_mutating,none,
41174119
"'@asyncHandler' function cannot be 'mutating'",
41184120
())
41194121

4122+
ERROR(objc_ambiguous_async_convention,none,
4123+
"%0 overrides or implements protocol requirements for Objective-C "
4124+
"declarations with incompatible async conventions",
4125+
(DeclName))
4126+
NOTE(objc_ambiguous_async_convention_candidate,none,
4127+
"%0 provides async here", (DeclName))
4128+
4129+
ERROR(satisfy_async_objc,none,
4130+
"satisfying an asychronous @objc %select{method|initializer}0 with "
4131+
"a synchronous %select{method|initializer}0 is not supported", (bool))
4132+
41204133
//------------------------------------------------------------------------------
41214134
// MARK: Type Check Types
41224135
//------------------------------------------------------------------------------

include/swift/AST/ForeignAsyncConvention.h

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,53 +24,96 @@ namespace swift {
2424

2525
/// A small structure describing the async convention of a foreign declaration.
2626
class ForeignAsyncConvention {
27-
/// The index of the completion handler parameters.
28-
unsigned CompletionHandlerParamIndex;
29-
30-
/// When non-zero, indicates which parameter to the completion handler is the
31-
/// Error? parameter (minus one) that makes this async function also throwing.
32-
unsigned CompletionHandlerErrorParamIndex;
3327
public:
34-
ForeignAsyncConvention()
28+
struct Info {
29+
/// The index of the completion handler parameters.
30+
unsigned CompletionHandlerParamIndex;
31+
32+
/// When non-zero, indicates which parameter to the completion handler is
33+
/// the Error? parameter (minus one) that makes this async function also
34+
/// throwing.
35+
unsigned CompletionHandlerErrorParamIndex;
36+
37+
Info()
3538
: CompletionHandlerParamIndex(0), CompletionHandlerErrorParamIndex(0) { }
3639

37-
ForeignAsyncConvention(unsigned completionHandlerParamIndex,
38-
Optional<unsigned> completionHandlerErrorParamIndex)
40+
Info(
41+
unsigned completionHandlerParamIndex,
42+
Optional<unsigned> completionHandlerErrorParamIndex)
3943
: CompletionHandlerParamIndex(completionHandlerParamIndex),
4044
CompletionHandlerErrorParamIndex(
41-
completionHandlerErrorParamIndex
42-
? *completionHandlerErrorParamIndex + 1
43-
: 0) {}
45+
completionHandlerErrorParamIndex
46+
? *completionHandlerErrorParamIndex + 1
47+
: 0) {}
48+
49+
/// Retrieve the index of the \c Error? parameter in the completion handler's
50+
/// parameter list. When argument passed to this parameter is non-null, the
51+
/// provided error will be thrown by the async function.
52+
Optional<unsigned> completionHandlerErrorParamIndex() const {
53+
if (CompletionHandlerErrorParamIndex == 0)
54+
return None;
55+
56+
return CompletionHandlerErrorParamIndex - 1;
57+
}
58+
59+
/// Whether the async function is throwing due to the completion handler
60+
/// having an \c Error? parameter.
61+
///
62+
/// Equivalent to \c static_cast<bool>(completionHandlerErrorParamIndex()).
63+
bool isThrowing() const {
64+
return CompletionHandlerErrorParamIndex != 0;
65+
}
66+
};
67+
68+
/// The type of the completion handler parameter.
69+
CanType CompletionHandlerType;
70+
71+
/// Information about the async convention that can be determined from an
72+
/// Objective-C declaration by itself.
73+
Info TheInfo;
74+
75+
public:
76+
ForeignAsyncConvention() : TheInfo() { }
77+
78+
ForeignAsyncConvention(CanType completionHandlerType,
79+
unsigned completionHandlerParamIndex,
80+
Optional<unsigned> completionHandlerErrorParamIndex)
81+
: CompletionHandlerType(completionHandlerType),
82+
TheInfo(completionHandlerParamIndex, completionHandlerErrorParamIndex)
83+
{ }
84+
85+
/// Retrieve the type of the completion handler parameter.
86+
CanType completionHandlerType() const { return CompletionHandlerType; }
4487

4588
/// Retrieve the index of the completion handler parameter, which will be
4689
/// erased from the Swift signature of the imported async function.
4790
unsigned completionHandlerParamIndex() const {
48-
return CompletionHandlerParamIndex;
91+
return TheInfo.CompletionHandlerParamIndex;
4992
}
5093

5194
/// Retrieve the index of the \c Error? parameter in the completion handler's
5295
/// parameter list. When argument passed to this parameter is non-null, the
5396
/// provided error will be thrown by the async function.
5497
Optional<unsigned> completionHandlerErrorParamIndex() const {
55-
if (CompletionHandlerErrorParamIndex == 0)
56-
return None;
57-
58-
return CompletionHandlerErrorParamIndex - 1;
98+
return TheInfo.completionHandlerErrorParamIndex();
5999
}
60100

61101
/// Whether the async function is throwing due to the completion handler
62102
/// having an \c Error? parameter.
63103
///
64104
/// Equivalent to \c static_cast<bool>(completionHandlerErrorParamIndex()).
65105
bool isThrowing() const {
66-
return CompletionHandlerErrorParamIndex != 0;
106+
return TheInfo.isThrowing();
67107
}
68108

69109
bool operator==(ForeignAsyncConvention other) const {
70-
return CompletionHandlerParamIndex == other.CompletionHandlerParamIndex
71-
&& CompletionHandlerErrorParamIndex ==
72-
other.CompletionHandlerErrorParamIndex;
110+
return CompletionHandlerType == other.CompletionHandlerType
111+
&& TheInfo.CompletionHandlerParamIndex ==
112+
other.TheInfo.CompletionHandlerParamIndex
113+
&& TheInfo.CompletionHandlerErrorParamIndex ==
114+
other.TheInfo.CompletionHandlerErrorParamIndex;
73115
}
116+
74117
bool operator!=(ForeignAsyncConvention other) const {
75118
return !(*this == other);
76119
}

lib/AST/ASTDumper.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "swift/AST/ASTPrinter.h"
1919
#include "swift/AST/ASTVisitor.h"
2020
#include "swift/AST/ClangModuleLoader.h"
21+
#include "swift/AST/ForeignAsyncConvention.h"
2122
#include "swift/AST/ForeignErrorConvention.h"
2223
#include "swift/AST/GenericEnvironment.h"
2324
#include "swift/AST/Initializer.h"
@@ -955,6 +956,16 @@ namespace {
955956
D->getCaptureInfo().print(OS);
956957
}
957958

959+
if (auto fac = D->getForeignAsyncConvention()) {
960+
OS << " foreign_async=";
961+
if (auto type = fac->completionHandlerType())
962+
type.print(OS);
963+
OS << ",completion_handler_param="
964+
<< fac->completionHandlerParamIndex();
965+
if (auto errorParamIndex = fac->completionHandlerErrorParamIndex())
966+
OS << ",error_param=" << *errorParamIndex;
967+
}
968+
958969
if (auto fec = D->getForeignErrorConvention()) {
959970
OS << " foreign_error=";
960971
OS << getForeignErrorConventionKindString(fec->getKind());

lib/AST/ASTVerifier.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/Decl.h"
2121
#include "swift/AST/ExistentialLayout.h"
2222
#include "swift/AST/Expr.h"
23+
#include "swift/AST/ForeignAsyncConvention.h"
2324
#include "swift/AST/ForeignErrorConvention.h"
2425
#include "swift/AST/GenericEnvironment.h"
2526
#include "swift/AST/GenericSignature.h"
@@ -2994,8 +2995,23 @@ class Verifier : public ASTWalker {
29942995
}
29952996
}
29962997

2997-
// Throwing @objc methods must have a foreign error convention.
2998+
// Asynchronous @objc methods must have a foreign async convention.
29982999
if (AFD->isObjC() &&
3000+
static_cast<bool>(AFD->getForeignAsyncConvention())
3001+
!= AFD->hasAsync()) {
3002+
if (AFD->hasAsync())
3003+
Out << "@objc method async but does not have a foreign async "
3004+
<< "convention";
3005+
else
3006+
Out << "@objc method has a foreign async convention but is not "
3007+
<< "async";
3008+
abort();
3009+
}
3010+
3011+
// Synchronous throwing @objc methods must have a foreign error
3012+
// convention.
3013+
if (AFD->isObjC() &&
3014+
!AFD->hasAsync() &&
29993015
static_cast<bool>(AFD->getForeignErrorConvention())
30003016
!= AFD->hasThrows()) {
30013017
if (AFD->hasThrows())

lib/AST/Decl.cpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "swift/AST/DiagnosticsSema.h"
2525
#include "swift/AST/ExistentialLayout.h"
2626
#include "swift/AST/Expr.h"
27+
#include "swift/AST/ForeignAsyncConvention.h"
2728
#include "swift/AST/ForeignErrorConvention.h"
2829
#include "swift/AST/GenericEnvironment.h"
2930
#include "swift/AST/GenericSignature.h"
@@ -6910,10 +6911,13 @@ AbstractFunctionDecl::getObjCSelector(DeclName preferredName,
69106911
}
69116912

69126913
// The number of selector pieces we'll have.
6914+
Optional<ForeignAsyncConvention> asyncConvention
6915+
= getForeignAsyncConvention();
69136916
Optional<ForeignErrorConvention> errorConvention
69146917
= getForeignErrorConvention();
69156918
unsigned numSelectorPieces
6916-
= argNames.size() + (errorConvention.hasValue() ? 1 : 0);
6919+
= argNames.size() + (asyncConvention.hasValue() ? 1 : 0)
6920+
+ (errorConvention.hasValue() ? 1 : 0);
69176921

69186922
// If we have no arguments, it's a nullary selector.
69196923
if (numSelectorPieces == 0) {
@@ -6932,6 +6936,14 @@ AbstractFunctionDecl::getObjCSelector(DeclName preferredName,
69326936
unsigned argIndex = 0;
69336937
for (unsigned piece = 0; piece != numSelectorPieces; ++piece) {
69346938
if (piece > 0) {
6939+
// If we have an async convention that inserts a completion handler
6940+
// parameter here, add "completionHandler".
6941+
if (asyncConvention &&
6942+
piece == asyncConvention->completionHandlerParamIndex()) {
6943+
selectorPieces.push_back(ctx.getIdentifier("completionHandler"));
6944+
continue;
6945+
}
6946+
69356947
// If we have an error convention that inserts an error parameter
69366948
// here, add "error".
69376949
if (errorConvention &&
@@ -6945,12 +6957,21 @@ AbstractFunctionDecl::getObjCSelector(DeclName preferredName,
69456957
continue;
69466958
}
69476959

6948-
// For the first selector piece, attach either the first parameter
6949-
// or "AndReturnError" to the base name, if appropriate.
6960+
// For the first selector piece, attach either the first parameter,
6961+
// "withCompletionHandker", or "AndReturnError" to the base name,
6962+
// if appropriate.
69506963
auto firstPiece = baseName;
69516964
llvm::SmallString<32> scratch;
69526965
scratch += firstPiece.str();
6953-
if (errorConvention && piece == errorConvention->getErrorParameterIndex()) {
6966+
if (asyncConvention &&
6967+
piece == asyncConvention->completionHandlerParamIndex()) {
6968+
// The completion handler is first; append "WithCompletionHandler".
6969+
camel_case::appendSentenceCase(scratch, "WithCompletionHandler");
6970+
6971+
firstPiece = ctx.getIdentifier(scratch);
6972+
didStringManipulation = true;
6973+
} else if (errorConvention &&
6974+
piece == errorConvention->getErrorParameterIndex()) {
69546975
// The error is first; append "AndReturnError".
69556976
camel_case::appendSentenceCase(scratch, "AndReturnError");
69566977

lib/ClangImporter/ImportDecl.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4380,6 +4380,7 @@ namespace {
43804380
kind = SpecialMethodKind::NSDictionarySubscriptGetter;
43814381

43824382
// Import the type that this method will have.
4383+
Optional<ForeignAsyncConvention> asyncConvention;
43834384
Optional<ForeignErrorConvention> errorConvention;
43844385

43854386
// If we have a property accessor, find the corresponding property
@@ -4418,7 +4419,7 @@ namespace {
44184419
importedType = Impl.importMethodParamsAndReturnType(
44194420
dc, decl, decl->parameters(), decl->isVariadic(),
44204421
isInSystemModule(dc), &bodyParams, importedName,
4421-
errorConvention, kind);
4422+
asyncConvention, errorConvention, kind);
44224423
}
44234424
if (!importedType)
44244425
return nullptr;
@@ -4440,10 +4441,10 @@ namespace {
44404441
// Determine whether the function is throwing and/or async.
44414442
bool throws = importedName.getErrorInfo().hasValue();
44424443
bool async = false;
4443-
auto asyncConvention = importedName.getAsyncInfo();
4444-
if (asyncConvention) {
4444+
auto asyncInfo = importedName.getAsyncInfo();
4445+
if (asyncInfo) {
44454446
async = true;
4446-
if (asyncConvention->isThrowing())
4447+
if (asyncInfo->isThrowing())
44474448
throws = true;
44484449
}
44494450

@@ -6381,11 +6382,14 @@ ConstructorDecl *SwiftDeclConverter::importConstructor(
63816382
assert(ownerNominal && "Method in non-type context?");
63826383

63836384
// Import the type that this method will have.
6385+
Optional<ForeignAsyncConvention> asyncConvention;
63846386
Optional<ForeignErrorConvention> errorConvention;
63856387
ParameterList *bodyParams;
63866388
auto importedType = Impl.importMethodParamsAndReturnType(
63876389
dc, objcMethod, args, variadic, isInSystemModule(dc), &bodyParams,
6388-
importedName, errorConvention, SpecialMethodKind::Constructor);
6390+
importedName, asyncConvention, errorConvention,
6391+
SpecialMethodKind::Constructor);
6392+
assert(!asyncConvention && "Initializers don't have async conventions");
63896393
if (!importedType)
63906394
return nullptr;
63916395

lib/ClangImporter/ImportName.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,7 +1185,7 @@ static bool isNullableNSErrorType(
11851185
return true;
11861186
}
11871187

1188-
Optional<ForeignAsyncConvention>
1188+
Optional<ForeignAsyncConvention::Info>
11891189
NameImporter::considerAsyncImport(
11901190
const clang::ObjCMethodDecl *clangDecl,
11911191
StringRef &baseName,
@@ -1211,7 +1211,8 @@ NameImporter::considerAsyncImport(
12111211
// Determine whether the naming indicates that this is a completion
12121212
// handler.
12131213
Optional<StringRef> newBaseName;
1214-
if (isCompletionHandlerParamName(paramNames[completionHandlerParamNameIndex])) {
1214+
if (isCompletionHandlerParamName(
1215+
paramNames[completionHandlerParamNameIndex])) {
12151216
// The argument label itself has an appropriate name.
12161217
} else if (!hasCustomName && completionHandlerParamIndex == 0 &&
12171218
(newBaseName = isCompletionHandlerInBaseName(baseName))) {
@@ -1226,7 +1227,8 @@ NameImporter::considerAsyncImport(
12261227
// Used for returns once we've determined that the method cannot be
12271228
// imported as async, even though it has what looks like a completion handler
12281229
// parameter.
1229-
auto notAsync = [&](const char *reason) -> Optional<ForeignAsyncConvention> {
1230+
auto notAsync = [&](const char *reason) ->
1231+
Optional<ForeignAsyncConvention::Info> {
12301232
#ifdef ASYNC_IMPORT_DEBUG
12311233
llvm::errs() << "*** failed async import: " << reason << "\n";
12321234
clangDecl->dump(llvm::errs());
@@ -1306,7 +1308,7 @@ NameImporter::considerAsyncImport(
13061308
if (newBaseName && !hasCustomName)
13071309
baseName = *newBaseName;
13081310

1309-
return ForeignAsyncConvention(
1311+
return ForeignAsyncConvention::Info(
13101312
completionHandlerParamIndex, completionHandlerErrorParamIndex);
13111313
}
13121314

lib/ClangImporter/ImportName.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ class ImportedName {
200200

201201
/// For names that map Objective-C completion handlers into async
202202
/// Swift methods, describes how the mapping is performed.
203-
ForeignAsyncConvention asyncInfo;
203+
ForeignAsyncConvention::Info asyncInfo;
204204

205205
/// For a declaration name that makes the declaration into an
206206
/// instance member, the index of the "Self" parameter.
@@ -270,7 +270,7 @@ class ImportedName {
270270

271271
/// For names that map Objective-C methods with completion handlers into
272272
/// async Swift methods, describes how the mapping is performed.
273-
Optional<ForeignAsyncConvention> getAsyncInfo() const {
273+
Optional<ForeignAsyncConvention::Info> getAsyncInfo() const {
274274
if (info.hasAsyncInfo)
275275
return info.asyncInfo;
276276
return None;
@@ -453,7 +453,7 @@ class NameImporter {
453453
ArrayRef<const clang::ParmVarDecl *> params,
454454
bool isInitializer, bool hasCustomName);
455455

456-
Optional<ForeignAsyncConvention>
456+
Optional<ForeignAsyncConvention::Info>
457457
considerAsyncImport(const clang::ObjCMethodDecl *clangDecl,
458458
StringRef &baseName,
459459
SmallVectorImpl<StringRef> &paramNames,

0 commit comments

Comments
 (0)