Skip to content

Commit 7f73877

Browse files
committed
[Concurrency] Parse 'async let' declarations.
Perform very basic semantic analysis for their well-formedness.
1 parent 5b8e514 commit 7f73877

File tree

9 files changed

+120
-1
lines changed

9 files changed

+120
-1
lines changed

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,12 @@ SIMPLE_DECL_ATTR(_specializeExtension, SpecializeExtension,
588588
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
589589
105)
590590

591+
CONTEXTUAL_SIMPLE_DECL_ATTR(async, Async,
592+
DeclModifier | OnVar | OnFunc | ConcurrencyOnly |
593+
ABIBreakingToAdd | ABIBreakingToRemove |
594+
APIBreakingToAdd | APIBreakingToRemove,
595+
106)
596+
591597
#undef TYPE_ATTR
592598
#undef DECL_ATTR_ALIAS
593599
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/DiagnosticsParse.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,8 @@ ERROR(func_decl_expected_arrow,none,
324324
ERROR(operator_static_in_protocol,none,
325325
"operator '%0' declared in protocol must be 'static'",
326326
(StringRef))
327+
ERROR(async_func_modifier,none,
328+
"'async' must be written after the parameter list of a function", ())
327329

328330
// Enum
329331
ERROR(expected_lbrace_enum,PointsToFirstBadToken,

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4118,6 +4118,15 @@ NOTE(protocol_witness_async_conflict,none,
41184118
ERROR(async_autoclosure_nonasync_function,none,
41194119
"'async' autoclosure parameter in a non-'async' function", ())
41204120

4121+
ERROR(async_not_let,none,
4122+
"'async' can only be used with 'let' declarations", ())
4123+
ERROR(async_let_not_local,none,
4124+
"'async let' can only be used on local declarations", ())
4125+
ERROR(async_let_not_initialized,none,
4126+
"'async let' binding requires an initializer expression", ())
4127+
ERROR(async_let_no_variables,none,
4128+
"'async let' requires at least one named variable", ())
4129+
41214130
ERROR(asynchandler_non_func,none,
41224131
"'@asyncHandler' can only be applied to functions",
41234132
())

lib/Parse/ParseDecl.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6558,6 +6558,19 @@ ParserResult<FuncDecl> Parser::parseDeclFunc(SourceLoc StaticLoc,
65586558

65596559
diagnoseWhereClauseInGenericParamList(GenericParams);
65606560

6561+
// If there was an 'async' modifier, put it in the right place for a function.
6562+
bool isAsync = asyncLoc.isValid();
6563+
if (auto asyncAttr = Attributes.getAttribute<AsyncAttr>()) {
6564+
SourceLoc insertLoc = Lexer::getLocForEndOfToken(
6565+
SourceMgr, BodyParams->getRParenLoc());
6566+
6567+
diagnose(asyncAttr->getLocation(), diag::async_func_modifier)
6568+
.fixItRemove(asyncAttr->getRange())
6569+
.fixItInsert(insertLoc, " async");
6570+
asyncAttr->setInvalid();
6571+
isAsync = true;
6572+
}
6573+
65616574
// Create the decl for the func and add it to the parent scope.
65626575
auto *FD = FuncDecl::create(Context, StaticLoc, StaticSpelling,
65636576
FuncLoc, FullName, NameLoc,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,59 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
344344

345345
(void)nominal->isGlobalActor();
346346
}
347+
348+
void visitAsyncAttr(AsyncAttr *attr) {
349+
auto var = dyn_cast<VarDecl>(D);
350+
if (!var)
351+
return;
352+
353+
auto patternBinding = var->getParentPatternBinding();
354+
if (!patternBinding)
355+
return; // already diagnosed
356+
357+
// "Async" modifier can only be applied to local declarations.
358+
if (!patternBinding->getDeclContext()->isLocalContext()) {
359+
diagnoseAndRemoveAttr(attr, diag::async_let_not_local);
360+
return;
361+
}
362+
363+
// Check each of the pattern binding entries.
364+
bool diagnosedVar = false;
365+
for (unsigned index : range(patternBinding->getNumPatternEntries())) {
366+
auto pattern = patternBinding->getPattern(index);
367+
368+
// Look for variables bound by this pattern.
369+
bool foundAnyVariable = false;
370+
bool isLet = true;
371+
pattern->forEachVariable([&](VarDecl *var) {
372+
if (!var->isLet())
373+
isLet = false;
374+
foundAnyVariable = true;
375+
});
376+
377+
// Each entry must bind at least one named variable, so that there is
378+
// something to "await".
379+
if (!foundAnyVariable) {
380+
diagnose(pattern->getLoc(), diag::async_let_no_variables);
381+
attr->setInvalid();
382+
return;
383+
}
384+
385+
// Async can only be used on an "async let".
386+
if (!isLet && !diagnosedVar) {
387+
diagnose(patternBinding->getLoc(), diag::async_not_let)
388+
.fixItReplace(patternBinding->getLoc(), "let");
389+
diagnosedVar = true;
390+
}
391+
392+
// Each pattern entry must have an initializer expression.
393+
if (patternBinding->getEqualLoc(index).isInvalid()) {
394+
diagnose(pattern->getLoc(), diag::async_let_not_initialized);
395+
attr->setInvalid();
396+
return;
397+
}
398+
}
399+
}
347400
};
348401
} // end anonymous namespace
349402

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,7 @@ namespace {
15161516
UNINTERESTING_ATTR(Actor)
15171517
UNINTERESTING_ATTR(ActorIndependent)
15181518
UNINTERESTING_ATTR(GlobalActor)
1519+
UNINTERESTING_ATTR(Async)
15191520
#undef UNINTERESTING_ATTR
15201521

15211522
void visitAvailableAttr(AvailableAttr *attr) {

test/Parse/async.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,12 @@ func testAwaitExpr() async {
5454
let myFuture = MyFuture()
5555
let _ = myFuture.await()
5656
}
57+
58+
func getIntSomeday() async -> Int { 5 }
59+
60+
func testAsyncLet() async {
61+
async let x = await getIntSomeday()
62+
_ = x
63+
}
64+
65+
async func asyncIncorrectly() { } // expected-error{{'async' must be written after the parameter list of a function}}{{1-7=}}{{30-30= async}}

test/decl/var/async_let.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// RUN: %target-typecheck-verify-swift -enable-experimental-concurrency
2+
3+
// REQUIRES: concurrency
4+
5+
async let x = 1 // okay
6+
7+
struct X {
8+
async let x = 1 // expected-error{{'async let' can only be used on local declarations}}
9+
}
10+
11+
func testAsyncFunc() async {
12+
async let (z1, z2) = (2, 3)
13+
async let (_, _) = (2, 3)
14+
async let x2 = 1
15+
16+
async var x = 17 // expected-error{{'async' can only be used with 'let' declarations}}{{9-12=let}}
17+
async let (_, _) = (1, 2), y2 = 7 // expected-error{{'async let' requires at least one named variable}}
18+
async let y: Int // expected-error{{'async let' binding requires an initializer expression}}
19+
_ = x
20+
_ = y
21+
_ = z1
22+
_ = z2
23+
_ = x2
24+
x = 1
25+
_ = y2
26+
}

utils/gyb_syntax_support/DeclNodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@
171171
'required', 'static', 'unowned', 'weak', 'private',
172172
'fileprivate', 'internal', 'public', 'open',
173173
'mutating', 'nonmutating', 'indirect', '__consuming',
174-
'actor'
174+
'actor', 'async'
175175
]),
176176
Child('DetailLeftParen', kind='LeftParenToken', is_optional=True),
177177
Child('Detail', kind='IdentifierToken', is_optional=True),

0 commit comments

Comments
 (0)