Skip to content

Commit ffbbbaf

Browse files
authored
Merge pull request #38052 from ahoppen/pr/use-closure-label-for-async-return-type
[Refactoring] Use internal completion handler labels for async function's return type
2 parents a4aa712 + d944b8b commit ffbbbaf

File tree

2 files changed

+123
-10
lines changed

2 files changed

+123
-10
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4079,6 +4079,16 @@ class HandlerResult {
40794079
/// single parameter of `Result` type.
40804080
enum class HandlerType { INVALID, PARAMS, RESULT };
40814081

4082+
/// A single return type of a refactored async function. If the async function
4083+
/// returns a tuple, each element of the tuple (represented by a \c
4084+
/// LabeledReturnType) might have a label, otherwise the \p Label is empty.
4085+
struct LabeledReturnType {
4086+
Identifier Label;
4087+
swift::Type Ty;
4088+
4089+
LabeledReturnType(Identifier Label, swift::Type Ty) : Label(Label), Ty(Ty) {}
4090+
};
4091+
40824092
/// Given a function with an async alternative (or one that *could* have an
40834093
/// async alternative), stores information about the completion handler.
40844094
/// The completion handler can be either a variable (which includes a parameter)
@@ -4327,12 +4337,26 @@ struct AsyncHandlerDesc {
43274337
}
43284338
}
43294339

4340+
/// If the async function returns a tuple, the label of the \p Index -th
4341+
/// element in the returned tuple. If the function doesn't return a tuple or
4342+
/// the element is unlabeled, an empty identifier is returned.
4343+
Identifier getAsyncReturnTypeLabel(size_t Index) const {
4344+
assert(Index < getSuccessParams().size());
4345+
if (getSuccessParams().size() <= 1) {
4346+
// There can't be any labels if the async function doesn't return a tuple.
4347+
return Identifier();
4348+
} else {
4349+
return getSuccessParams()[Index].getInternalLabel();
4350+
}
4351+
}
4352+
43304353
/// Gets the return value types for the async equivalent of this handler.
4331-
ArrayRef<swift::Type>
4332-
getAsyncReturnTypes(SmallVectorImpl<swift::Type> &Scratch) const {
4333-
for (auto &Param : getSuccessParams()) {
4334-
auto Ty = Param.getParameterType();
4335-
Scratch.push_back(getSuccessParamAsyncReturnType(Ty));
4354+
ArrayRef<LabeledReturnType>
4355+
getAsyncReturnTypes(SmallVectorImpl<LabeledReturnType> &Scratch) const {
4356+
for (size_t I = 0; I < getSuccessParams().size(); ++I) {
4357+
auto Ty = getSuccessParams()[I].getParameterType();
4358+
Scratch.emplace_back(getAsyncReturnTypeLabel(I),
4359+
getSuccessParamAsyncReturnType(Ty));
43364360
}
43374361
return Scratch;
43384362
}
@@ -6371,7 +6395,7 @@ class AsyncConverter : private SourceEntityWalker {
63716395
return;
63726396
}
63736397

6374-
SmallVector<Type, 2> Scratch;
6398+
SmallVector<LabeledReturnType, 2> Scratch;
63756399
auto ReturnTypes = TopHandler.getAsyncReturnTypes(Scratch);
63766400
if (ReturnTypes.empty()) {
63776401
OS << " ";
@@ -6385,7 +6409,14 @@ class AsyncConverter : private SourceEntityWalker {
63856409
OS << "(";
63866410

63876411
llvm::interleave(
6388-
ReturnTypes, [&](Type Ty) { Ty->print(OS); }, [&]() { OS << ", "; });
6412+
ReturnTypes,
6413+
[&](LabeledReturnType TypeAndLabel) {
6414+
if (!TypeAndLabel.Label.empty()) {
6415+
OS << TypeAndLabel.Label << tok::colon << " ";
6416+
}
6417+
TypeAndLabel.Ty->print(OS);
6418+
},
6419+
[&]() { OS << ", "; });
63896420

63906421
if (ReturnTypes.size() > 1)
63916422
OS << ")";
@@ -7164,7 +7195,14 @@ class AsyncConverter : private SourceEntityWalker {
71647195
// completion(result.0, result.1)
71657196
// }
71667197
// }
7167-
OS << ResultName << tok::period << Index;
7198+
OS << ResultName << tok::period;
7199+
7200+
auto Label = HandlerDesc.getAsyncReturnTypeLabel(Index);
7201+
if (!Label.empty()) {
7202+
OS << Label;
7203+
} else {
7204+
OS << Index;
7205+
}
71687206
} else {
71697207
OS << ResultName;
71707208
}
@@ -7212,9 +7250,14 @@ class AsyncConverter : private SourceEntityWalker {
72127250
/// returned results via a completion handler described by \p HandlerDesc.
72137251
void addAsyncFuncReturnType(const AsyncHandlerDesc &HandlerDesc) {
72147252
// Type or (Type1, Type2, ...)
7215-
SmallVector<Type, 2> Scratch;
7253+
SmallVector<LabeledReturnType, 2> Scratch;
72167254
addTupleOf(HandlerDesc.getAsyncReturnTypes(Scratch), OS,
7217-
[&](auto Ty) { Ty->print(OS); });
7255+
[&](LabeledReturnType LabelAndType) {
7256+
if (!LabelAndType.Label.empty()) {
7257+
OS << LabelAndType.Label << tok::colon << " ";
7258+
}
7259+
LabelAndType.Ty->print(OS);
7260+
});
72187261
}
72197262

72207263
/// If \p FD is generic, adds a type annotation with the return type of the
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-LABELED-RESULTS %s
4+
func mutlipleLabeledResults(completion: (_ first: String, _ second: String) -> Void) { }
5+
// MULTIPLE-LABELED-RESULTS: {
6+
// MULTIPLE-LABELED-RESULTS-NEXT: async {
7+
// MULTIPLE-LABELED-RESULTS-NEXT: let result = await mutlipleLabeledResults()
8+
// MULTIPLE-LABELED-RESULTS-NEXT: completion(result.first, result.second)
9+
// MULTIPLE-LABELED-RESULTS-NEXT: }
10+
// MULTIPLE-LABELED-RESULTS-NEXT: }
11+
// MULTIPLE-LABELED-RESULTS: func mutlipleLabeledResults() async -> (first: String, second: String) { }
12+
13+
// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MIXED-LABELED-RESULTS %s
14+
func mixedLabeledResult(completion: (_ first: String, String) -> Void) { }
15+
// MIXED-LABELED-RESULTS: {
16+
// MIXED-LABELED-RESULTS-NEXT: async {
17+
// MIXED-LABELED-RESULTS-NEXT: let result = await mixedLabeledResult()
18+
// MIXED-LABELED-RESULTS-NEXT: completion(result.first, result.1)
19+
// MIXED-LABELED-RESULTS-NEXT: }
20+
// MIXED-LABELED-RESULTS-NEXT: }
21+
// MIXED-LABELED-RESULTS: func mixedLabeledResult() async -> (first: String, String) { }
22+
23+
// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SINGLE-LABELED-RESULT %s
24+
func singleLabeledResult(completion: (_ first: String) -> Void) { }
25+
// SINGLE-LABELED-RESULT: {
26+
// SINGLE-LABELED-RESULT-NEXT: async {
27+
// SINGLE-LABELED-RESULT-NEXT: let result = await singleLabeledResult()
28+
// SINGLE-LABELED-RESULT-NEXT: completion(result)
29+
// SINGLE-LABELED-RESULT-NEXT: }
30+
// SINGLE-LABELED-RESULT-NEXT: }
31+
// SINGLE-LABELED-RESULT: func singleLabeledResult() async -> String { }
32+
33+
// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=SINGLE-LABELED-RESULT-WITH-ERROR %s
34+
func singleLabeledResultWithError(completion: (_ first: String?, _ error: Error?) -> Void) { }
35+
// SINGLE-LABELED-RESULT-WITH-ERROR: {
36+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: async {
37+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: do {
38+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: let result = try await singleLabeledResultWithError()
39+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(result, nil)
40+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: } catch {
41+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(nil, error)
42+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: }
43+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: }
44+
// SINGLE-LABELED-RESULT-WITH-ERROR-NEXT: }
45+
// SINGLE-LABELED-RESULT-WITH-ERROR: func singleLabeledResultWithError() async throws -> String { }
46+
47+
// RUN: %refactor-check-compiles -add-async-alternative -dump-text -source-filename %s -pos=%(line+1):1 | %FileCheck -check-prefix=MULTIPLE-LABELED-RESULT-WITH-ERROR %s
48+
func multipleLabeledResultWithError(completion: (_ first: String?, _ second: String?, _ error: Error?) -> Void) { }
49+
// MULTIPLE-LABELED-RESULT-WITH-ERROR: {
50+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: async {
51+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: do {
52+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: let result = try await multipleLabeledResultWithError()
53+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(result.first, result.second, nil)
54+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: } catch {
55+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: completion(nil, nil, error)
56+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: }
57+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: }
58+
// MULTIPLE-LABELED-RESULT-WITH-ERROR-NEXT: }
59+
// MULTIPLE-LABELED-RESULT-WITH-ERROR: func multipleLabeledResultWithError() async throws -> (first: String, second: String) { }
60+
61+
func testConvertCall() {
62+
// RUN: %refactor -convert-call-to-async-alternative -dump-text -source-filename %s -pos=%(line+1):3 | %FileCheck -check-prefix=CONVERT-CALL %s
63+
mutlipleLabeledResults() { (a, b) in
64+
print(a)
65+
print(b)
66+
}
67+
// CONVERT-CALL: let (a, b) = await mutlipleLabeledResults()
68+
// CONVERT-CALL-NEXT: print(a)
69+
// CONVERT-CALL-NEXT: print(b)
70+
}

0 commit comments

Comments
 (0)