@@ -4150,6 +4150,14 @@ struct AsyncHandlerDesc {
4150
4150
return Ty->getParams ();
4151
4151
}
4152
4152
4153
+ // / Retrieve the parameters relevant to a successful return from the
4154
+ // / completion handler. This drops the Error parameter if present.
4155
+ ArrayRef<AnyFunctionType::Param> getSuccessParams () const {
4156
+ if (HasError && Type == HandlerType::PARAMS)
4157
+ return params ().drop_back ();
4158
+ return params ();
4159
+ }
4160
+
4153
4161
// / The `CallExpr` if the given node is a call to the `Handler`
4154
4162
CallExpr *getAsHandlerCall (ASTNode Node) const {
4155
4163
if (!isValid ())
@@ -4171,13 +4179,16 @@ struct AsyncHandlerDesc {
4171
4179
auto Args = ArgList.ref ();
4172
4180
4173
4181
if (Type == HandlerType::PARAMS) {
4174
- if (!HasError)
4175
- return HandlerResult (Args);
4176
-
4177
- if (!isa<NilLiteralExpr>(Args.back ()))
4182
+ // If there's an error parameter and the user isn't passing nil to it,
4183
+ // assume this is the error path.
4184
+ if (HasError && !isa<NilLiteralExpr>(Args.back ()))
4178
4185
return HandlerResult (Args.back (), true );
4179
4186
4180
- return HandlerResult (Args.drop_back ());
4187
+ // We can drop the args altogether if they're just Void.
4188
+ if (willAsyncReturnVoid ())
4189
+ return HandlerResult ();
4190
+
4191
+ return HandlerResult (HasError ? Args.drop_back () : Args);
4181
4192
} else if (Type == HandlerType::RESULT) {
4182
4193
if (Args.size () != 1 )
4183
4194
return HandlerResult (Args);
@@ -4196,12 +4207,62 @@ struct AsyncHandlerDesc {
4196
4207
return HandlerResult (Args);
4197
4208
4198
4209
auto ResultArgList = callArgs (ResultCE);
4199
- return HandlerResult (ResultArgList.ref ()[0 ],
4200
- D->getNameStr () == StringRef (" failure" ));
4210
+ auto isFailure = D->getNameStr () == StringRef (" failure" );
4211
+
4212
+ // We can drop the arg altogether if it's just Void.
4213
+ if (!isFailure && willAsyncReturnVoid ())
4214
+ return HandlerResult ();
4215
+
4216
+ // Otherwise the arg gets the .success() or .failure() call dropped.
4217
+ return HandlerResult (ResultArgList.ref ()[0 ], isFailure);
4201
4218
}
4202
4219
4203
4220
llvm_unreachable (" Unhandled result type" );
4204
4221
}
4222
+
4223
+ // Convert the type of a success parameter in the completion handler function
4224
+ // to a return type suitable for an async function. If there is an error
4225
+ // parameter present e.g (T?, Error?) -> Void, this unwraps a level of
4226
+ // optionality from T?. If this is a Result<T, U> type, returns the success
4227
+ // type T.
4228
+ swift::Type getSuccessParamAsyncReturnType (swift::Type Ty) const {
4229
+ switch (Type) {
4230
+ case HandlerType::PARAMS: {
4231
+ // If there's an Error parameter in the handler, the success branch can
4232
+ // be unwrapped.
4233
+ if (HasError)
4234
+ Ty = Ty->lookThroughSingleOptionalType ();
4235
+
4236
+ return Ty;
4237
+ }
4238
+ case HandlerType::RESULT: {
4239
+ // Result<T, U> maps to T.
4240
+ return Ty->castTo <BoundGenericType>()->getGenericArgs ()[0 ];
4241
+ }
4242
+ case HandlerType::INVALID:
4243
+ llvm_unreachable (" Invalid handler type" );
4244
+ }
4245
+ }
4246
+
4247
+ // / Gets the return value types for the async equivalent of this handler.
4248
+ ArrayRef<swift::Type>
4249
+ getAsyncReturnTypes (SmallVectorImpl<swift::Type> &Scratch) const {
4250
+ for (auto &Param : getSuccessParams ()) {
4251
+ auto Ty = Param.getParameterType ();
4252
+ Scratch.push_back (getSuccessParamAsyncReturnType (Ty));
4253
+ }
4254
+ return Scratch;
4255
+ }
4256
+
4257
+ // / Whether the async equivalent of this handler returns Void.
4258
+ bool willAsyncReturnVoid () const {
4259
+ // If all of the success params will be converted to Void return types,
4260
+ // this will be a Void async function.
4261
+ return llvm::all_of (getSuccessParams (), [&](auto ¶m) {
4262
+ auto Ty = param.getParameterType ();
4263
+ return getSuccessParamAsyncReturnType (Ty)->isVoid ();
4264
+ });
4265
+ }
4205
4266
};
4206
4267
4207
4268
enum class ConditionType { INVALID, NIL, NOT_NIL };
@@ -4919,42 +4980,24 @@ class AsyncConverter : private SourceEntityWalker {
4919
4980
return ;
4920
4981
}
4921
4982
4922
- auto HandlerParams = TopHandler.params ();
4923
- if (TopHandler.Type == HandlerType::PARAMS && TopHandler.HasError ) {
4924
- HandlerParams = HandlerParams.drop_back ();
4925
- }
4926
-
4927
- if (HandlerParams.empty ()) {
4983
+ SmallVector<Type, 2 > Scratch;
4984
+ auto ReturnTypes = TopHandler.getAsyncReturnTypes (Scratch);
4985
+ if (ReturnTypes.empty ()) {
4928
4986
OS << " " ;
4929
4987
return ;
4930
4988
}
4931
4989
4932
- OS << " -> " ;
4990
+ // Print the function result type, making sure to omit a '-> Void' return.
4991
+ if (!TopHandler.willAsyncReturnVoid ()) {
4992
+ OS << " -> " ;
4993
+ if (ReturnTypes.size () > 1 )
4994
+ OS << " (" ;
4933
4995
4934
- if (HandlerParams.size () > 1 ) {
4935
- OS << " (" ;
4936
- }
4937
- for (size_t I = 0 , E = HandlerParams.size (); I < E; ++I) {
4938
- if (I > 0 ) {
4939
- OS << " , " ;
4940
- }
4996
+ llvm::interleave (
4997
+ ReturnTypes, [&](Type Ty) { Ty->print (OS); }, [&]() { OS << " , " ; });
4941
4998
4942
- auto &Param = HandlerParams[I];
4943
- if (TopHandler.Type == HandlerType::PARAMS) {
4944
- Type ToPrint = Param.getPlainType ();
4945
- if (TopHandler.HasError )
4946
- ToPrint = ToPrint->lookThroughSingleOptionalType ();
4947
- ToPrint->print (OS);
4948
- } else if (TopHandler.Type == HandlerType::RESULT) {
4949
- auto ResultTy = Param.getPlainType ()->getAs <BoundGenericType>();
4950
- assert (ResultTy && " Result must have generic type" );
4951
- ResultTy->getGenericArgs ()[0 ]->print (OS);
4952
- } else {
4953
- llvm_unreachable (" Unhandled handler type" );
4954
- }
4955
- }
4956
- if (HandlerParams.size () > 1 ) {
4957
- OS << " )" ;
4999
+ if (ReturnTypes.size () > 1 )
5000
+ OS << " )" ;
4958
5001
}
4959
5002
4960
5003
if (FD->hasBody ())
@@ -5081,8 +5124,7 @@ class AsyncConverter : private SourceEntityWalker {
5081
5124
addFallbackVars (CallbackParams->getArray (), Blocks);
5082
5125
addDo ();
5083
5126
addAwaitCall (CE, ArgList.ref (), Blocks.SuccessBlock , SuccessParams,
5084
- /* HasError=*/ HandlerDesc.HasError ,
5085
- /* AddDeclarations=*/ !HandlerDesc.HasError );
5127
+ HandlerDesc, /* AddDeclarations=*/ !HandlerDesc.HasError );
5086
5128
addFallbackCatch (ErrParam);
5087
5129
OS << " \n " ;
5088
5130
convertNodes (CallbackBody);
@@ -5115,10 +5157,9 @@ class AsyncConverter : private SourceEntityWalker {
5115
5157
5116
5158
setNames (Blocks.SuccessBlock , SuccessParams);
5117
5159
addAwaitCall (CE, ArgList.ref (), Blocks.SuccessBlock , SuccessParams,
5118
- /* HasError=*/ HandlerDesc.HasError ,
5119
- /* AddDeclarations=*/ true );
5160
+ HandlerDesc, /* AddDeclarations=*/ true );
5120
5161
5121
- prepareNamesForBody (HandlerDesc. Type , SuccessParams, ErrParams);
5162
+ prepareNamesForBody (HandlerDesc, SuccessParams, ErrParams);
5122
5163
convertNodes (Blocks.SuccessBlock .nodes ());
5123
5164
5124
5165
if (RequireDo) {
@@ -5128,7 +5169,7 @@ class AsyncConverter : private SourceEntityWalker {
5128
5169
HandlerDesc.Type != HandlerType::RESULT);
5129
5170
addCatch (ErrParam);
5130
5171
5131
- prepareNamesForBody (HandlerDesc. Type , ErrParams, SuccessParams);
5172
+ prepareNamesForBody (HandlerDesc, ErrParams, SuccessParams);
5132
5173
addCatchBody (ErrParam, Blocks.ErrorBlock );
5133
5174
}
5134
5175
@@ -5137,9 +5178,11 @@ class AsyncConverter : private SourceEntityWalker {
5137
5178
5138
5179
void addAwaitCall (const CallExpr *CE, ArrayRef<Expr *> Args,
5139
5180
const ClassifiedBlock &SuccessBlock,
5140
- ArrayRef<const ParamDecl *> SuccessParams, bool HasError,
5141
- bool AddDeclarations) {
5142
- if (!SuccessParams.empty ()) {
5181
+ ArrayRef<const ParamDecl *> SuccessParams,
5182
+ const AsyncHandlerDesc &HandlerDesc, bool AddDeclarations) {
5183
+ // Print the bindings to match the completion handler success parameters,
5184
+ // making sure to omit in the case of a Void return.
5185
+ if (!SuccessParams.empty () && !HandlerDesc.willAsyncReturnVoid ()) {
5143
5186
if (AddDeclarations) {
5144
5187
if (SuccessBlock.allLet ()) {
5145
5188
OS << tok::kw_let;
@@ -5161,7 +5204,7 @@ class AsyncConverter : private SourceEntityWalker {
5161
5204
OS << " " << tok::equal << " " ;
5162
5205
}
5163
5206
5164
- if (HasError) {
5207
+ if (HandlerDesc. HasError ) {
5165
5208
OS << tok::kw_try << " " ;
5166
5209
}
5167
5210
OS << " await " ;
@@ -5207,16 +5250,21 @@ class AsyncConverter : private SourceEntityWalker {
5207
5250
OS << " \n " << tok::r_brace;
5208
5251
}
5209
5252
5210
- void prepareNamesForBody (HandlerType ResultType ,
5253
+ void prepareNamesForBody (const AsyncHandlerDesc &HandlerDesc ,
5211
5254
ArrayRef<const ParamDecl *> CurrentParams,
5212
5255
ArrayRef<const ParamDecl *> OtherParams) {
5213
- switch (ResultType ) {
5256
+ switch (HandlerDesc. Type ) {
5214
5257
case HandlerType::PARAMS:
5215
5258
for (auto *Param : CurrentParams) {
5216
- if (Param->getType ()->getOptionalObjectType ()) {
5259
+ auto Ty = Param->getType ();
5260
+ if (Ty->getOptionalObjectType ()) {
5217
5261
Unwraps.insert (Param);
5218
5262
Placeholders.insert (Param);
5219
5263
}
5264
+ // Void parameters get omitted where possible, so turn any reference
5265
+ // into a placeholder, as its usage is unlikely what the user wants.
5266
+ if (HandlerDesc.getSuccessParamAsyncReturnType (Ty)->isVoid ())
5267
+ Placeholders.insert (Param);
5220
5268
}
5221
5269
// Use of the other params is invalid within the current body
5222
5270
Placeholders.insert (OtherParams.begin (), OtherParams.end ());
0 commit comments