Skip to content

Commit 3edaa70

Browse files
committed
Augment ForeignAsyncConvention with fields for completion handler flag params.
When the requisite support in Clang for `__attribute__((swift_async_error))` parameters lands, this will let us represent APIs that take completion handlers in the general shape of `void (^)(BOOL, id, NSError*)`, where the boolean argument indicates the presence of an error rather than the nilness of the `NSError*` argument.
1 parent 1f3879b commit 3edaa70

File tree

8 files changed

+122
-19
lines changed

8 files changed

+122
-19
lines changed

include/swift/AST/ForeignAsyncConvention.h

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,24 +30,35 @@ class ForeignAsyncConvention {
3030
/// The index of the completion handler parameters.
3131
unsigned CompletionHandlerParamIndex;
3232

33-
/// When non-zero, indicates which parameter to the completion handler is
34-
/// the Error? parameter (minus one) that makes this async function also
33+
/// When non-zero, subtracting one indicates which parameter to the completion handler is
34+
/// the Error? parameter that makes this async function also
3535
/// throwing.
3636
unsigned CompletionHandlerErrorParamIndexPlusOneOrZero;
3737

38+
/// When non-zero, indicates that the presence of an error is determined by
39+
/// an integral argument to the completion handler being zero or nonzero.
40+
unsigned CompletionHandlerFlagParamIndexPlusOneWithPolarityOrZero;
41+
3842
public:
3943
Info()
4044
: CompletionHandlerParamIndex(0),
4145
CompletionHandlerErrorParamIndexPlusOneOrZero(0) { }
4246

4347
Info(
4448
unsigned completionHandlerParamIndex,
45-
Optional<unsigned> completionHandlerErrorParamIndex)
49+
Optional<unsigned> completionHandlerErrorParamIndex,
50+
Optional<unsigned> completionHandlerFlagParamIndex,
51+
bool completionHandlerFlagIsErrorOnZero)
4652
: CompletionHandlerParamIndex(completionHandlerParamIndex),
4753
CompletionHandlerErrorParamIndexPlusOneOrZero(
4854
completionHandlerErrorParamIndex
4955
? *completionHandlerErrorParamIndex + 1
50-
: 0) {}
56+
: 0),
57+
CompletionHandlerFlagParamIndexPlusOneWithPolarityOrZero(
58+
completionHandlerFlagParamIndex
59+
? (*completionHandlerFlagParamIndex | ((unsigned)completionHandlerFlagIsErrorOnZero << 31)) + 1
60+
: 0)
61+
{}
5162

5263
/// Retrieve the index of the completion handler argument in the method's
5364
/// parameter list.
@@ -56,15 +67,46 @@ class ForeignAsyncConvention {
5667
}
5768

5869
/// Retrieve the index of the \c Error? parameter in the completion handler's
59-
/// parameter list. When argument passed to this parameter is non-null, the
60-
/// provided error will be thrown by the async function.
70+
/// parameter list.
71+
///
72+
/// Typically, when argument passed to this parameter is non-null, the
73+
/// provided error will be thrown by the async function. If a
74+
/// \c completionHandlerFlagParamIndex is also specified, the
75+
/// value of that flag instead indicates whether an error should be raised.
6176
Optional<unsigned> completionHandlerErrorParamIndex() const {
6277
if (CompletionHandlerErrorParamIndexPlusOneOrZero == 0)
6378
return None;
6479

6580
return CompletionHandlerErrorParamIndexPlusOneOrZero - 1;
6681
}
6782

83+
/// Retrieve the index of the error flag parameter in the completion handler's
84+
/// parameter list, if any.
85+
///
86+
/// If present, the boolean value of this argument will indicate whether the
87+
/// operation completed with an error. The \c completionHandlerFlagIsErrorOnZero
88+
/// value indicates whether this argument being zero indicates an error, or
89+
/// whether being nonzero indicates an error.
90+
Optional<unsigned> completionHandlerFlagParamIndex() const {
91+
if (CompletionHandlerFlagParamIndexPlusOneWithPolarityOrZero == 0)
92+
return None;
93+
94+
return (CompletionHandlerFlagParamIndexPlusOneWithPolarityOrZero - 1)
95+
& 0x7FFFFFFFu;
96+
}
97+
98+
/// Indicates the polarity of the error flag parameter to the completion handler.
99+
///
100+
/// It is only valid to call this if \c completionHandlerFlagParamIndex returns
101+
/// a non-\c None value; if there is no flag parameter to the completion handler, the value
102+
/// of this property is meaningless. Otherwise, if true is returned, then a zero flag value
103+
/// indicates an error, and nonzero indicates success. If false, then a zero flag value
104+
/// indicates success, and nonzero indicates an error.
105+
bool completionHandlerFlagIsErrorOnZero() const {
106+
return (CompletionHandlerFlagParamIndexPlusOneWithPolarityOrZero - 1)
107+
& 0x80000000u;
108+
}
109+
68110
/// Whether the async function is throwing due to the completion handler
69111
/// having an \c Error? parameter.
70112
///
@@ -86,9 +128,13 @@ class ForeignAsyncConvention {
86128

87129
ForeignAsyncConvention(CanType completionHandlerType,
88130
unsigned completionHandlerParamIndex,
89-
Optional<unsigned> completionHandlerErrorParamIndex)
131+
Optional<unsigned> completionHandlerErrorParamIndex,
132+
Optional<unsigned> completionHandlerFlagParamIndex,
133+
bool completionHandlerFlagIsErrorOnZero)
90134
: CompletionHandlerType(completionHandlerType),
91-
TheInfo(completionHandlerParamIndex, completionHandlerErrorParamIndex)
135+
TheInfo(completionHandlerParamIndex, completionHandlerErrorParamIndex,
136+
completionHandlerFlagParamIndex,
137+
completionHandlerFlagIsErrorOnZero)
92138
{ }
93139

94140
/// Retrieve the type of the completion handler parameter.
@@ -101,11 +147,36 @@ class ForeignAsyncConvention {
101147
}
102148

103149
/// Retrieve the index of the \c Error? parameter in the completion handler's
104-
/// parameter list. When argument passed to this parameter is non-null, the
105-
/// provided error will be thrown by the async function.
150+
/// parameter list.
151+
///
152+
/// Typically, when argument passed to this parameter is non-null, the
153+
/// provided error will be thrown by the async function. If a
154+
/// \c completionHandlerFlagParamIndex is also specified, the
155+
/// value of that flag instead indicates whether an error should be raised.
106156
Optional<unsigned> completionHandlerErrorParamIndex() const {
107157
return TheInfo.completionHandlerErrorParamIndex();
108158
}
159+
160+
/// Retrieve the index of the error flag parameter in the completion handler's
161+
/// parameter list, if any.
162+
///
163+
/// If present, the boolean value of this argument will indicate whether the
164+
/// operation completed with an error. The \c completionHandlerFlagIsErrorOnZero
165+
/// value indicates whether this argument being zero indicates an error, or
166+
/// whether being nonzero indicates an error.
167+
Optional<unsigned> completionHandlerFlagParamIndex() const {
168+
return TheInfo.completionHandlerFlagParamIndex();
169+
}
170+
171+
/// Indicates the polarity of the error flag parameter to the completion handler.
172+
///
173+
/// It is only valid to call this if \c completionHandlerFlagParamIndex returns
174+
/// a non-\c None value. If true is returned, then a zero flag value indicates an error,
175+
/// and nonzero indicates success. If false, then a zero flag value indicates success,
176+
/// and nonzero indicates an error.
177+
bool completionHandlerFlagIsErrorOnZero() const {
178+
return TheInfo.completionHandlerFlagIsErrorOnZero();
179+
}
109180

110181
/// Whether the async function is throwing due to the completion handler
111182
/// having an \c Error? parameter.

lib/ClangImporter/ImportName.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,8 @@ NameImporter::considerAsyncImport(
12351235
bool isInitializer,
12361236
Optional<unsigned> explicitCompletionHandlerParamIndex,
12371237
CustomAsyncName customName,
1238+
Optional<unsigned> completionHandlerFlagParamIndex,
1239+
bool completionHandlerFlagIsZeroOnError,
12381240
Optional<ForeignErrorConvention::Info> errorInfo) {
12391241
// If there are no unclaimed parameters, there's no .
12401242
unsigned errorParamAdjust = errorInfo ? 1 : 0;
@@ -1388,7 +1390,8 @@ NameImporter::considerAsyncImport(
13881390
}
13891391

13901392
return ForeignAsyncConvention::Info(
1391-
completionHandlerParamIndex, completionHandlerErrorParamIndex);
1393+
completionHandlerParamIndex, completionHandlerErrorParamIndex,
1394+
completionHandlerFlagParamIndex, completionHandlerFlagIsZeroOnError);
13921395
}
13931396

13941397
bool NameImporter::hasErrorMethodNameCollision(
@@ -1482,6 +1485,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
14821485

14831486
// Gather information from the swift_async attribute, if there is one.
14841487
Optional<unsigned> completionHandlerParamIndex;
1488+
bool completionHandlerFlagIsZeroOnError = false;
1489+
Optional<unsigned> completionHandlerFlagParamIndex;
14851490
if (version.supportsConcurrency()) {
14861491
if (const auto *swiftAsyncAttr = D->getAttr<clang::SwiftAsyncAttr>()) {
14871492
// If this is swift_async(none), don't import as async at all.
@@ -1492,6 +1497,9 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
14921497
completionHandlerParamIndex =
14931498
swiftAsyncAttr->getCompletionHandlerIndex().getASTIndex();
14941499
}
1500+
1501+
// TODO: Check for the swift_async_error attribute here when Clang
1502+
// implements it
14951503
}
14961504

14971505
// FIXME: ugly to check here, instead perform unified check up front in
@@ -1646,6 +1654,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
16461654
completionHandlerParamIndex,
16471655
nameAttr->isAsync ? CustomAsyncName::SwiftAsyncName
16481656
: CustomAsyncName::SwiftName,
1657+
completionHandlerFlagParamIndex,
1658+
completionHandlerFlagIsZeroOnError,
16491659
result.getErrorInfo())) {
16501660
result.info.hasAsyncInfo = true;
16511661
result.info.asyncInfo = *asyncInfo;
@@ -1934,6 +1944,8 @@ ImportedName NameImporter::importNameImpl(const clang::NamedDecl *D,
19341944
if (auto asyncInfo = considerAsyncImport(
19351945
objcMethod, baseName, argumentNames, params, isInitializer,
19361946
completionHandlerParamIndex, CustomAsyncName::None,
1947+
completionHandlerFlagParamIndex,
1948+
completionHandlerFlagIsZeroOnError,
19371949
result.getErrorInfo())) {
19381950
result.info.hasAsyncInfo = true;
19391951
result.info.asyncInfo = *asyncInfo;

lib/ClangImporter/ImportName.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ class NameImporter {
472472
bool isInitializer,
473473
Optional<unsigned> explicitCompletionHandlerParamIndex,
474474
CustomAsyncName customName,
475+
Optional<unsigned> completionHandlerFlagParamIndex,
476+
bool completionHandlerFlagIsZeroOnError,
475477
Optional<ForeignErrorConvention::Info> errorInfo);
476478

477479
EffectiveClangContext determineEffectiveContext(const clang::NamedDecl *,

lib/ClangImporter/ImportType.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2470,7 +2470,9 @@ ImportedType ClangImporter::Implementation::importMethodParamsAndReturnType(
24702470
if (asyncInfo) {
24712471
asyncConvention = ForeignAsyncConvention(
24722472
completionHandlerType, asyncInfo->completionHandlerParamIndex(),
2473-
asyncInfo->completionHandlerErrorParamIndex());
2473+
asyncInfo->completionHandlerErrorParamIndex(),
2474+
asyncInfo->completionHandlerFlagParamIndex(),
2475+
asyncInfo->completionHandlerFlagIsErrorOnZero());
24742476
}
24752477

24762478
if (errorInfo) {

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -762,7 +762,8 @@ bool swift::isRepresentableInObjC(
762762

763763
asyncConvention = ForeignAsyncConvention(
764764
completionHandlerType->getCanonicalType(), completionHandlerParamIndex,
765-
completionHandlerErrorParamIndex);
765+
completionHandlerErrorParamIndex,
766+
/* no flag argument */ None, false);
766767
} else if (AFD->hasThrows()) {
767768
// Synchronous throwing functions must map to a particular error convention.
768769
DeclContext *dc = const_cast<AbstractFunctionDecl *>(AFD);

lib/Serialization/Deserialization.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6527,22 +6527,31 @@ Optional<ForeignAsyncConvention> ModuleFile::maybeReadForeignAsyncConvention() {
65276527
TypeID completionHandlerTypeID;
65286528
unsigned completionHandlerParameterIndex;
65296529
unsigned rawErrorParameterIndex;
6530+
unsigned rawErrorFlagParameterIndex;
6531+
bool errorFlagPolarity;
65306532
ForeignAsyncConventionLayout::readRecord(scratch,
65316533
completionHandlerTypeID,
65326534
completionHandlerParameterIndex,
6533-
rawErrorParameterIndex);
6535+
rawErrorParameterIndex,
6536+
rawErrorFlagParameterIndex,
6537+
errorFlagPolarity);
65346538

65356539
Type completionHandlerType = getType(completionHandlerTypeID);
65366540
CanType canCompletionHandlerType;
65376541
if (completionHandlerType)
65386542
canCompletionHandlerType = completionHandlerType->getCanonicalType();
65396543

6540-
// Decode the error parameter.
6544+
// Decode the error and flag parameters.
65416545
Optional<unsigned> completionHandlerErrorParamIndex;
65426546
if (rawErrorParameterIndex > 0)
65436547
completionHandlerErrorParamIndex = rawErrorParameterIndex - 1;
6548+
Optional<unsigned> completionHandlerErrorFlagParamIndex;
6549+
if (rawErrorFlagParameterIndex > 0)
6550+
completionHandlerErrorFlagParamIndex = rawErrorFlagParameterIndex - 1;
65446551

65456552
return ForeignAsyncConvention(
65466553
canCompletionHandlerType, completionHandlerParameterIndex,
6547-
completionHandlerErrorParamIndex);
6554+
completionHandlerErrorParamIndex,
6555+
completionHandlerErrorFlagParamIndex,
6556+
errorFlagPolarity);
65486557
}

lib/Serialization/ModuleFormat.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ const uint16_t SWIFTMODULE_VERSION_MAJOR = 0;
5656
/// describe what change you made. The content of this comment isn't important;
5757
/// it just ensures a conflict if two people change the module format.
5858
/// Don't worry about adhering to the 80-column limit for this line.
59-
const uint16_t SWIFTMODULE_VERSION_MINOR = 595; // Adding Actor class decls
59+
const uint16_t SWIFTMODULE_VERSION_MINOR = 596; // Add flag parameter index to ForeignAsyncConvention
6060

6161
/// A standard hash seed used for all string hashes in a serialized module.
6262
///
@@ -1774,7 +1774,9 @@ namespace decls_block {
17741774
FOREIGN_ASYNC_CONVENTION,
17751775
TypeIDField, // completion handler type
17761776
BCVBR<4>, // completion handler parameter index
1777-
BCVBR<4> // completion handler error parameter index (+1)
1777+
BCVBR<4>, // completion handler error parameter index (+1)
1778+
BCVBR<4>, // completion handler error flag parameter index (+1)
1779+
BCFixed<1> // completion handler error flag polarity
17781780
>;
17791781

17801782
using AbstractClosureExprLayout = BCRecordLayout<

lib/Serialization/Serialization.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2716,11 +2716,15 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
27162716
TypeID completionHandlerTypeID = S.addTypeRef(fac.completionHandlerType());
27172717
unsigned rawErrorParameterIndex = fac.completionHandlerErrorParamIndex()
27182718
.map([](unsigned index) { return index + 1; }).getValueOr(0);
2719+
unsigned rawErrorFlagParameterIndex = fac.completionHandlerFlagParamIndex()
2720+
.map([](unsigned index) { return index + 1; }).getValueOr(0);
27192721
auto abbrCode = S.DeclTypeAbbrCodes[ForeignAsyncConventionLayout::Code];
27202722
ForeignAsyncConventionLayout::emitRecord(S.Out, S.ScratchRecord, abbrCode,
27212723
completionHandlerTypeID,
27222724
fac.completionHandlerParamIndex(),
2723-
rawErrorParameterIndex);
2725+
rawErrorParameterIndex,
2726+
rawErrorFlagParameterIndex,
2727+
fac.completionHandlerFlagIsErrorOnZero());
27242728
}
27252729

27262730
void writeGenericParams(const GenericParamList *genericParams) {

0 commit comments

Comments
 (0)