Skip to content

Commit 5013d20

Browse files
authored
Merge pull request #35784 from DougGregor/se-0296
2 parents 44f6214 + 089151c commit 5013d20

File tree

11 files changed

+68
-139
lines changed

11 files changed

+68
-139
lines changed

CHANGELOG.md

+25
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,30 @@ CHANGELOG
2525

2626
</details>
2727

28+
Swift Next
29+
----------
30+
* [SE-0296][]:
31+
32+
Asynchronous programming is now natively supported using async/await. Asynchronous functions can be defined using `async`:
33+
34+
```swift
35+
func loadWebResource(_ path: String) async throws -> Resource { ... }
36+
func decodeImage(_ r1: Resource, _ r2: Resource) async throws -> Image
37+
func dewarpAndCleanupImage(_ i : Image) async -> Image
38+
```
39+
40+
Calls to `async` functions may suspend, meaning that they give up the thread on which they are executing and will be scheduled to run again later. The potential for suspension on asynchronous calls requires the `await` keyword, similarly to the way in which `try` acknowledges a call to a `throws` function:
41+
42+
```swift
43+
func processImageData() async throws -> Image {
44+
let dataResource = try await loadWebResource("dataprofile.txt")
45+
let imageResource = try await loadWebResource("imagedata.dat")
46+
let imageTmp = try await decodeImage(dataResource, imageResource)
47+
let imageResult = await dewarpAndCleanupImage(imageTmp)
48+
return imageResult
49+
}
50+
```
51+
2852
Swift 5.4
2953
---------
3054
@@ -8178,6 +8202,7 @@ Swift 1.0
81788202
[SE-0284]: <https://github.com/apple/swift-evolution/blob/main/proposals/0284-multiple-variadic-parameters.md>
81798203
[SE-0286]: <https://github.com/apple/swift-evolution/blob/main/proposals/0286-forward-scan-trailing-closures.md>
81808204
[SE-0287]: <https://github.com/apple/swift-evolution/blob/main/proposals/0287-implicit-member-chains.md>
8205+
[SE-0296]: <https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md>
81818206

81828207
[SR-75]: <https://bugs.swift.org/browse/SR-75>
81838208
[SR-106]: <https://bugs.swift.org/browse/SR-106>

include/swift/AST/DiagnosticsParse.def

-3
Original file line numberDiff line numberDiff line change
@@ -1229,9 +1229,6 @@ NOTE(super_in_closure_with_capture_here,none,
12291229
"'self' explicitly captured here", ())
12301230

12311231
WARNING(await_before_try,none, "'try' must precede 'await'", ())
1232-
WARNING(warn_await_keyword,none,
1233-
"future versions of Swift reserve the word 'await'; "
1234-
"if this name is unavoidable, use backticks to escape it", ())
12351232

12361233
// Tuples and parenthesized expressions
12371234
ERROR(expected_expr_in_expr_list,none,

lib/IDE/CodeCompletion.cpp

+5-9
Original file line numberDiff line numberDiff line change
@@ -5818,15 +5818,12 @@ static void addObserverKeywords(CodeCompletionResultSink &Sink) {
58185818
addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None);
58195819
}
58205820

5821-
static void addExprKeywords(CodeCompletionResultSink &Sink,
5822-
bool IsConcurrencyEnabled) {
5821+
static void addExprKeywords(CodeCompletionResultSink &Sink) {
58235822
// Expr keywords.
58245823
addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try);
58255824
addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try);
58265825
addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try);
5827-
if (IsConcurrencyEnabled) {
5828-
addKeyword(Sink, "await", CodeCompletionKeywordKind::None);
5829-
}
5826+
addKeyword(Sink, "await", CodeCompletionKeywordKind::None);
58305827
}
58315828

58325829
static void addOpaqueTypeKeyword(CodeCompletionResultSink &Sink) {
@@ -5865,8 +5862,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
58655862
break;
58665863

58675864
case CompletionKind::EffectsSpecifier: {
5868-
if (!llvm::is_contained(ParsedKeywords, "async") &&
5869-
Context.LangOpts.EnableExperimentalConcurrency)
5865+
if (!llvm::is_contained(ParsedKeywords, "async"))
58705866
addKeyword(Sink, "async", CodeCompletionKeywordKind::None);
58715867
if (!llvm::is_contained(ParsedKeywords, "throws"))
58725868
addKeyword(Sink, "throws", CodeCompletionKeywordKind::kw_throws);
@@ -5902,7 +5898,7 @@ void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink,
59025898
case CompletionKind::ForEachSequence:
59035899
addSuperKeyword(Sink);
59045900
addLetVarKeywords(Sink);
5905-
addExprKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency);
5901+
addExprKeywords(Sink);
59065902
addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType);
59075903
break;
59085904

@@ -6729,7 +6725,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
67296725
addStmtKeywords(Sink, MaybeFuncBody);
67306726
addSuperKeyword(Sink);
67316727
addLetVarKeywords(Sink);
6732-
addExprKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency);
6728+
addExprKeywords(Sink);
67336729
addAnyTypeKeyword(Sink, Context.TheAnyType);
67346730
DoPostfixExprBeginning();
67356731
}

lib/IDE/Refactoring.cpp

-9
Original file line numberDiff line numberDiff line change
@@ -5219,9 +5219,6 @@ bool RefactoringActionConvertCallToAsyncAlternative::isApplicable(
52195219
const ResolvedCursorInfo &CursorInfo, DiagnosticEngine &Diag) {
52205220
using namespace asyncrefactorings;
52215221

5222-
if (!CursorInfo.SF->getASTContext().LangOpts.EnableExperimentalConcurrency)
5223-
return false;
5224-
52255222
// Currently doesn't check that the call is in an async context. This seems
52265223
// possibly useful in some situations, so we'll see what the feedback is.
52275224
// May need to change in the future
@@ -5264,9 +5261,6 @@ bool RefactoringActionConvertToAsync::isApplicable(
52645261
const ResolvedCursorInfo &CursorInfo, DiagnosticEngine &Diag) {
52655262
using namespace asyncrefactorings;
52665263

5267-
if (!CursorInfo.SF->getASTContext().LangOpts.EnableExperimentalConcurrency)
5268-
return false;
5269-
52705264
// As with the call refactoring, should possibly only apply if there's
52715265
// actually calls to async alternatives. At the moment this will just add
52725266
// `async` if there are no calls, which is probably fine.
@@ -5297,9 +5291,6 @@ bool RefactoringActionAddAsyncAlternative::isApplicable(
52975291
const ResolvedCursorInfo &CursorInfo, DiagnosticEngine &Diag) {
52985292
using namespace asyncrefactorings;
52995293

5300-
if (!CursorInfo.SF->getASTContext().LangOpts.EnableExperimentalConcurrency)
5301-
return false;
5302-
53035294
auto *FD = findFunction(CursorInfo);
53045295
if (!FD)
53055296
return false;

lib/Parse/ParseExpr.cpp

+25-31
Original file line numberDiff line numberDiff line change
@@ -337,8 +337,7 @@ ParserResult<Expr> Parser::parseExprSequence(Diag<> Message,
337337
case tok::identifier: {
338338
// 'async' followed by 'throws' or '->' implies that we have an arrow
339339
// expression.
340-
if (!(shouldParseExperimentalConcurrency() &&
341-
Tok.isContextualKeyword("async") &&
340+
if (!(Tok.isContextualKeyword("async") &&
342341
peekToken().isAny(tok::arrow, tok::kw_throws)))
343342
goto done;
344343

@@ -399,38 +398,33 @@ ParserResult<Expr> Parser::parseExprSequenceElement(Diag<> message,
399398
SyntaxParsingContext ElementContext(SyntaxContext,
400399
SyntaxContextKind::Expr);
401400

402-
if (shouldParseExperimentalConcurrency()) {
403-
// A function called "async" is possible, so we don't want to replace it
404-
// with await.
405-
bool isReplaceableAsync = Tok.isContextualKeyword("async") &&
406-
!peekToken().is(tok::l_paren);
407-
if (Tok.isContextualKeyword("await") || isReplaceableAsync) {
408-
// Error on a replaceable async
409-
if (isReplaceableAsync) {
410-
diagnose(Tok.getLoc(), diag::expected_await_not_async)
411-
.fixItReplace(Tok.getLoc(), "await");
412-
}
413-
SourceLoc awaitLoc = consumeToken();
414-
ParserResult<Expr> sub =
415-
parseExprSequenceElement(diag::expected_expr_after_await, isExprBasic);
416-
if (!sub.hasCodeCompletion() && !sub.isNull()) {
417-
if (auto anyTry = dyn_cast<AnyTryExpr>(sub.get())) {
418-
// "try" must precede "await".
419-
diagnose(awaitLoc, diag::await_before_try)
420-
.fixItRemove(awaitLoc)
421-
.fixItInsert(anyTry->getSubExpr()->getStartLoc(), "await ");
422-
}
423-
424-
ElementContext.setCreateSyntax(SyntaxKind::AwaitExpr);
425-
sub = makeParserResult(new (Context) AwaitExpr(awaitLoc, sub.get()));
401+
// Check whether the user mistyped "async" for "await", but only in cases
402+
// where we are sure that "async" would be ill-formed as an identifier.
403+
bool isReplaceableAsync = Tok.isContextualKeyword("async") &&
404+
!peekToken().isAtStartOfLine() &&
405+
(peekToken().is(tok::identifier) || peekToken().is(tok::kw_try));
406+
if (Tok.isContextualKeyword("await") || isReplaceableAsync) {
407+
// Error on a replaceable async
408+
if (isReplaceableAsync) {
409+
diagnose(Tok.getLoc(), diag::expected_await_not_async)
410+
.fixItReplace(Tok.getLoc(), "await");
411+
}
412+
SourceLoc awaitLoc = consumeToken();
413+
ParserResult<Expr> sub =
414+
parseExprSequenceElement(diag::expected_expr_after_await, isExprBasic);
415+
if (!sub.hasCodeCompletion() && !sub.isNull()) {
416+
if (auto anyTry = dyn_cast<AnyTryExpr>(sub.get())) {
417+
// "try" must precede "await".
418+
diagnose(awaitLoc, diag::await_before_try)
419+
.fixItRemove(awaitLoc)
420+
.fixItInsert(anyTry->getSubExpr()->getStartLoc(), "await ");
426421
}
427422

428-
return sub;
423+
ElementContext.setCreateSyntax(SyntaxKind::AwaitExpr);
424+
sub = makeParserResult(new (Context) AwaitExpr(awaitLoc, sub.get()));
429425
}
430-
} else if (Tok.isContextualKeyword("await")) {
431-
// warn that future versions of Swift will parse this token differently.
432-
diagnose(Tok.getLoc(), diag::warn_await_keyword)
433-
.fixItReplace(Tok.getLoc(), "`await`");
426+
427+
return sub;
434428
}
435429

436430
SourceLoc tryLoc;

lib/Parse/ParsePattern.cpp

+2-4
Original file line numberDiff line numberDiff line change
@@ -825,8 +825,7 @@ bool Parser::isEffectsSpecifier(const Token &T) {
825825
// NOTE: If this returns 'true', that token must be handled in
826826
// 'parseEffectsSpecifiers()'.
827827

828-
if (shouldParseExperimentalConcurrency() &&
829-
T.isContextualKeyword("async"))
828+
if (T.isContextualKeyword("async"))
830829
return true;
831830

832831
if (T.isAny(tok::kw_throws, tok::kw_rethrows) ||
@@ -844,8 +843,7 @@ ParserStatus Parser::parseEffectsSpecifiers(SourceLoc existingArrowLoc,
844843

845844
while (true) {
846845
// 'async'
847-
if (shouldParseExperimentalConcurrency() &&
848-
Tok.isContextualKeyword("async")) {
846+
if (Tok.isContextualKeyword("async")) {
849847

850848
if (asyncLoc.isValid()) {
851849
diagnose(Tok, diag::duplicate_effects_specifier, Tok.getText())

lib/Parse/ParseStmt.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -2128,10 +2128,10 @@ ParserResult<Stmt> Parser::parseStmtForEach(LabeledStmtInfo LabelInfo) {
21282128
SourceLoc TryLoc;
21292129

21302130
if (shouldParseExperimentalConcurrency() &&
2131-
Tok.isContextualKeyword("await")) {
2131+
Tok.isContextualKeyword("await")) {
21322132
AwaitLoc = consumeToken();
2133-
} if (shouldParseExperimentalConcurrency() &&
2134-
Tok.is(tok::kw_try)) {
2133+
} else if (shouldParseExperimentalConcurrency() &&
2134+
Tok.is(tok::kw_try)) {
21352135
TryLoc = consumeToken();
21362136
if (Tok.isContextualKeyword("await")) {
21372137
AwaitLoc = consumeToken();

lib/Parse/ParseType.cpp

+1-2
Original file line numberDiff line numberDiff line change
@@ -1108,8 +1108,7 @@ ParserResult<TypeRepr> Parser::parseTypeTupleBody() {
11081108

11091109
bool isFunctionType =
11101110
Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows) ||
1111-
(shouldParseExperimentalConcurrency() &&
1112-
Tok.isContextualKeyword("async"));
1111+
Tok.isContextualKeyword("async");
11131112

11141113
// If there were any labels, figure out which labels should go into the type
11151114
// representation.

test/Compatibility/unescaped_await.swift

-68
This file was deleted.

test/Concurrency/await_typo_correction.swift

+5
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,8 @@ func async() throws { }
2929
try async()
3030
}
3131
}
32+
33+
func varNamedAsync(async: Bool) async {
34+
if async { }
35+
let _ = async
36+
}

test/SourceKit/Refactoring/basic.swift

+2-10
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,8 @@ func hasCallToAsyncAlternative() {
166166
// RUN: %sourcekitd-test -req=cursor -pos=117:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
167167
// RUN: %sourcekitd-test -req=cursor -pos=117:17 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
168168

169-
// RUN: %sourcekitd-test -req=cursor -pos=119:6 -cursor-action %s -- -Xfrontend -enable-experimental-concurrency %s | %FileCheck %s -check-prefix=CHECK-ASYNC
170-
// RUN: %sourcekitd-test -req=cursor -pos=121:3 -cursor-action %s -- -Xfrontend -enable-experimental-concurrency %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC
171-
// RUN: %sourcekitd-test -req=cursor -pos=119:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NOASYNC
172-
// RUN: %sourcekitd-test -req=cursor -pos=121:3 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-NOASYNC
169+
// RUN: %sourcekitd-test -req=cursor -pos=119:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-ASYNC
170+
// RUN: %sourcekitd-test -req=cursor -pos=121:3 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-CALLASYNC
173171

174172
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT
175173
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT
@@ -258,10 +256,4 @@ func hasCallToAsyncAlternative() {
258256
// CHECK-ASYNC-NOT: source.refactoring.kind.convert.func-to-async
259257
// CHECK-CALLASYNC: ACTIONS END
260258

261-
// CHECK-NOASYNC: ACTIONS BEGIN
262-
// CHECK-NOASYNC-NOT: source.refactoring.kind.add.async-alternative
263-
// CHECK-NOASYNC-NOT: source.refactoring.kind.convert.func-to-async
264-
// CHECK-NOASYNC-NOT: source.refactoring.kind.convert.call-to-async
265-
// CHECK-NOASYNC: ACTIONS END
266-
267259
// REQUIRES: OS=macosx || OS=linux-gnu

0 commit comments

Comments
 (0)