Skip to content

Commit a83c791

Browse files
authored
Merge pull request #37163 from DougGregor/spawn-let-5.5
Spawn let syntax
2 parents 8ecc3e0 + 493c867 commit a83c791

20 files changed

+118
-82
lines changed

include/swift/AST/Attr.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,12 @@ SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext,
657657
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove,
658658
116)
659659

660+
CONTEXTUAL_SIMPLE_DECL_ATTR(spawn, Spawn,
661+
DeclModifier | OnVar | ConcurrencyOnly |
662+
ABIBreakingToAdd | ABIBreakingToRemove |
663+
APIBreakingToAdd | APIBreakingToRemove,
664+
117)
665+
660666
#undef TYPE_ATTR
661667
#undef DECL_ATTR_ALIAS
662668
#undef CONTEXTUAL_DECL_ATTR_ALIAS

include/swift/AST/Decl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1863,7 +1863,7 @@ class PatternBindingDecl final : public Decl,
18631863
bool isComputingPatternBindingEntry(const VarDecl *vd) const;
18641864

18651865
/// Is this an "async let" declaration?
1866-
bool isAsyncLet() const;
1866+
bool isSpawnLet() const;
18671867

18681868
/// Gets the text of the initializer expression for the pattern entry at the
18691869
/// given index, stripping out inactive branches of any #ifs inside the
@@ -4943,7 +4943,7 @@ class VarDecl : public AbstractStorageDecl {
49434943
bool isLet() const { return getIntroducer() == Introducer::Let; }
49444944

49454945
/// Is this an "async let" property?
4946-
bool isAsyncLet() const;
4946+
bool isSpawnLet() const;
49474947

49484948
Introducer getIntroducer() const {
49494949
return Introducer(Bits.VarDecl.Introducer);

include/swift/AST/DiagnosticsSema.def

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4231,8 +4231,8 @@ ERROR(throwing_interpolation_without_try,none,
42314231
"interpolation can throw but is not marked with 'try'", ())
42324232
ERROR(throwing_call_without_try,none,
42334233
"call can throw but is not marked with 'try'", ())
4234-
ERROR(throwing_async_let_without_try,none,
4235-
"reading 'async let' can throw but is not marked with 'try'", ())
4234+
ERROR(throwing_spawn_let_without_try,none,
4235+
"reading 'spawn let' can throw but is not marked with 'try'", ())
42364236
ERROR(throwing_prop_access_without_try,none,
42374237
"property access can throw but is not marked with 'try'", ())
42384238
ERROR(throwing_subscript_access_without_try,none,
@@ -4261,8 +4261,8 @@ NOTE(async_access_without_await,none,
42614261

42624262
NOTE(async_call_without_await_in_autoclosure,none,
42634263
"call is 'async' in an autoclosure argument", ())
4264-
NOTE(async_call_without_await_in_async_let,none,
4265-
"call is 'async' in an 'async let' initializer", ())
4264+
NOTE(async_call_without_await_in_spawn_let,none,
4265+
"call is 'async' in an 'spawn let' initializer", ())
42664266

42674267
WARNING(no_async_in_await,none,
42684268
"no 'async' operations occur within 'await' expression", ())
@@ -4275,7 +4275,7 @@ ERROR(await_in_illegal_context,none,
42754275
"%select{<<ERROR>>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}0",
42764276
(unsigned))
42774277
ERROR(async_in_nonasync_function,none,
4278-
"%select{'async'|'async' call|'await'|'async let'|'async' property access|'async' subscript access}0 in "
4278+
"%select{'async'|'async' call|'await'|'spawn let'|'async' property access|'async' subscript access}0 in "
42794279
"%select{a function|an autoclosure}1 that does not support concurrency",
42804280
(unsigned, bool))
42814281
NOTE(note_add_async_to_function,none,
@@ -4302,18 +4302,21 @@ NOTE(protocol_witness_async_conflict,none,
43024302
ERROR(async_autoclosure_nonasync_function,none,
43034303
"'async' autoclosure parameter in a non-'async' function", ())
43044304

4305-
ERROR(async_not_let,none,
4306-
"'async' can only be used with 'let' declarations", ())
4307-
ERROR(async_let_not_local,none,
4308-
"'async let' can only be used on local declarations", ())
4309-
ERROR(async_let_not_initialized,none,
4310-
"'async let' binding requires an initializer expression", ())
4311-
ERROR(async_let_no_variables,none,
4312-
"'async let' requires at least one named variable", ())
4313-
NOTE(async_let_without_await,none,
4314-
"reference to async let %0 is 'async'", (DeclName))
4315-
ERROR(async_let_in_illegal_context,none,
4316-
"async let %0 cannot be referenced in "
4305+
WARNING(async_let_is_spawn_let,none,
4306+
"'async let' is now 'spawn let'", ())
4307+
4308+
ERROR(spawn_not_let,none,
4309+
"'spawn' can only be used with 'let' declarations", ())
4310+
ERROR(spawn_let_not_local,none,
4311+
"'spawn let' can only be used on local declarations", ())
4312+
ERROR(spawn_let_not_initialized,none,
4313+
"'spawn let' binding requires an initializer expression", ())
4314+
ERROR(spawn_let_no_variables,none,
4315+
"'spawn let' requires at least one named variable", ())
4316+
NOTE(spawn_let_without_await,none,
4317+
"reference to spawn let %0 is 'async'", (DeclName))
4318+
ERROR(spawn_let_in_illegal_context,none,
4319+
"spawn let %0 cannot be referenced in "
43174320
"%select{<<ERROR>>|a default argument|a property wrapper initializer|a property initializer|a global variable initializer|an enum case raw value|a catch pattern|a catch guard expression|a defer body}1",
43184321
(DeclName, unsigned))
43194322

@@ -4403,8 +4406,8 @@ ERROR(actor_isolated_from_concurrent_closure,none,
44034406
ERROR(actor_isolated_from_concurrent_function,none,
44044407
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from a concurrent function",
44054408
(DescriptiveDeclKind, DeclName, unsigned))
4406-
ERROR(actor_isolated_from_async_let,none,
4407-
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'async let' initializer",
4409+
ERROR(actor_isolated_from_spawn_let,none,
4410+
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'spawn let' initializer",
44084411
(DescriptiveDeclKind, DeclName, unsigned))
44094412
ERROR(actor_isolated_keypath_component,none,
44104413
"cannot form key path to actor-isolated %0 %1",

include/swift/Sema/ConstraintSystem.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4433,7 +4433,7 @@ class ConstraintSystem {
44334433
/// Given expression represents computed result of the closure.
44344434
Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType,
44354435
bool isDefaultWrappedValue = false,
4436-
bool isAsyncLetWrapper = false);
4436+
bool isSpawnLetWrapper = false);
44374437

44384438
/// Builds a type-erased return expression that can be used in dynamic
44394439
/// replacement.

lib/AST/Decl.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,9 +1567,9 @@ StaticSpellingKind PatternBindingDecl::getCorrectStaticSpelling() const {
15671567
return getCorrectStaticSpellingForDecl(this);
15681568
}
15691569

1570-
bool PatternBindingDecl::isAsyncLet() const {
1570+
bool PatternBindingDecl::isSpawnLet() const {
15711571
if (auto var = getAnchoringVarDecl(0))
1572-
return var->isAsyncLet();
1572+
return var->isSpawnLet();
15731573

15741574
return false;
15751575
}
@@ -5862,8 +5862,8 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
58625862
return true;
58635863
}
58645864

5865-
bool VarDecl::isAsyncLet() const {
5866-
return getAttrs().hasAttribute<AsyncAttr>();
5865+
bool VarDecl::isSpawnLet() const {
5866+
return getAttrs().hasAttribute<AsyncAttr>() || getAttrs().hasAttribute<SpawnAttr>();
58675867
}
58685868

58695869
void ParamDecl::setSpecifier(Specifier specifier) {

lib/SILGen/SILGenDecl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ class LetValueInitialization : public Initialization {
440440
// buffer. DI will make sure it is only assigned to once.
441441
needsTemporaryBuffer = true;
442442
isUninitialized = true;
443-
} else if (vd->isAsyncLet()) {
443+
} else if (vd->isSpawnLet()) {
444444
// If this is an async let, treat it like a let-value without an
445445
// initializer. The initializer runs concurrently in a child task,
446446
// and value will be initialized at the point the variable in the
@@ -1146,7 +1146,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
11461146

11471147
// If this is an async let, create a child task to compute the initializer
11481148
// value.
1149-
if (PBD->isAsyncLet()) {
1149+
if (PBD->isSpawnLet()) {
11501150
// Look through the implicit await (if present), try (if present), and
11511151
// call to reach the autoclosure that computes the value.
11521152
auto *init = PBD->getExecutableInit(idx);

lib/SILGen/SILGenLValue.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2816,7 +2816,7 @@ SILGenFunction::maybeEmitValueOfLocalVarDecl(
28162816
if (It != VarLocs.end()) {
28172817
// If the variable is part of an async let, ensure that the child task
28182818
// has completed first.
2819-
if (var->isAsyncLet() && accessKind != AccessKind::Write) {
2819+
if (var->isSpawnLet() && accessKind != AccessKind::Write) {
28202820
auto patternBinding = var->getParentPatternBinding();
28212821
unsigned index = patternBinding->getPatternEntryIndexForVarDecl(var);
28222822
completeAsyncLetChildTask(patternBinding, index);

lib/Sema/CSApply.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8297,7 +8297,7 @@ static Expr *wrapAsyncLetInitializer(
82978297
ASTContext &ctx = dc->getASTContext();
82988298
Expr *autoclosureExpr = cs.buildAutoClosureExpr(
82998299
initializer, closureType, /*isDefaultWrappedValue=*/false,
8300-
/*isAsyncLetWrapper=*/true);
8300+
/*isSpawnLetWrapper=*/true);
83018301

83028302
// Call the autoclosure so that the AST types line up. SILGen will ignore the
83038303
// actual calls and translate them into a different mechanism.
@@ -8398,7 +8398,7 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
83988398
// For an async let, wrap the initializer appropriately to make it a child
83998399
// task.
84008400
if (auto patternBinding = target.getInitializationPatternBindingDecl()) {
8401-
if (patternBinding->isAsyncLet()) {
8401+
if (patternBinding->isSpawnLet()) {
84028402
resultTarget.setExpr(
84038403
wrapAsyncLetInitializer(
84048404
cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2517,7 +2517,7 @@ FunctionType::ExtInfo ConstraintSystem::closureEffects(ClosureExpr *expr) {
25172517
bool walkToDeclPre(Decl *decl) override {
25182518
// Do not walk into function or type declarations.
25192519
if (auto *patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
2520-
if (patternBinding->isAsyncLet())
2520+
if (patternBinding->isSpawnLet())
25212521
FoundAsync = true;
25222522

25232523
return true;
@@ -4987,7 +4987,7 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion,
49874987
Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
49884988
FunctionType *closureType,
49894989
bool isDefaultWrappedValue,
4990-
bool isAsyncLetWrapper) {
4990+
bool isSpawnLetWrapper) {
49914991
auto &Context = DC->getASTContext();
49924992
bool isInDefaultArgumentContext = false;
49934993
if (auto *init = dyn_cast<Initializer>(DC)) {
@@ -5009,7 +5009,7 @@ Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
50095009

50105010
closure->setParameterList(ParameterList::createEmpty(Context));
50115011

5012-
if (isAsyncLetWrapper)
5012+
if (isSpawnLetWrapper)
50135013
closure->setThunkKind(AutoClosureExpr::Kind::AsyncLet);
50145014

50155015
Expr *result = closure;

lib/Sema/TypeCheckAttr.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,8 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
284284
void visitActorIndependentAttr(ActorIndependentAttr *attr);
285285
void visitGlobalActorAttr(GlobalActorAttr *attr);
286286
void visitAsyncAttr(AsyncAttr *attr);
287+
void visitSpawnAttr(SpawnAttr *attr);
288+
void visitAsyncOrSpawnAttr(DeclAttribute *attr);
287289
void visitMarkerAttr(MarkerAttr *attr);
288290

289291
void visitReasyncAttr(ReasyncAttr *attr);
@@ -5503,6 +5505,20 @@ void AttributeChecker::visitGlobalActorAttr(GlobalActorAttr *attr) {
55035505
}
55045506

55055507
void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) {
5508+
if (isa<VarDecl>(D)) {
5509+
D->getASTContext().Diags.diagnose(
5510+
attr->getLocation(), diag::async_let_is_spawn_let)
5511+
.fixItReplace(attr->getRange(), "spawn");
5512+
5513+
visitAsyncOrSpawnAttr(attr);
5514+
}
5515+
}
5516+
5517+
void AttributeChecker::visitSpawnAttr(SpawnAttr *attr) {
5518+
visitAsyncOrSpawnAttr(attr);
5519+
}
5520+
5521+
void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) {
55065522
auto var = dyn_cast<VarDecl>(D);
55075523
if (!var)
55085524
return;
@@ -5513,7 +5529,7 @@ void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) {
55135529

55145530
// "Async" modifier can only be applied to local declarations.
55155531
if (!patternBinding->getDeclContext()->isLocalContext()) {
5516-
diagnoseAndRemoveAttr(attr, diag::async_let_not_local);
5532+
diagnoseAndRemoveAttr(attr, diag::spawn_let_not_local);
55175533
return;
55185534
}
55195535

@@ -5534,21 +5550,21 @@ void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) {
55345550
// Each entry must bind at least one named variable, so that there is
55355551
// something to "await".
55365552
if (!foundAnyVariable) {
5537-
diagnose(pattern->getLoc(), diag::async_let_no_variables);
5553+
diagnose(pattern->getLoc(), diag::spawn_let_no_variables);
55385554
attr->setInvalid();
55395555
return;
55405556
}
55415557

55425558
// Async can only be used on an "async let".
55435559
if (!isLet && !diagnosedVar) {
5544-
diagnose(patternBinding->getLoc(), diag::async_not_let)
5560+
diagnose(patternBinding->getLoc(), diag::spawn_not_let)
55455561
.fixItReplace(patternBinding->getLoc(), "let");
55465562
diagnosedVar = true;
55475563
}
55485564

55495565
// Each pattern entry must have an initializer expression.
55505566
if (patternBinding->getEqualLoc(index).isInvalid()) {
5551-
diagnose(pattern->getLoc(), diag::async_let_not_initialized);
5567+
diagnose(pattern->getLoc(), diag::spawn_let_not_initialized);
55525568
attr->setInvalid();
55535569
return;
55545570
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,10 @@ namespace {
18791879
// Check whether this is a local variable, in which case we can
18801880
// determine whether it was safe to access concurrently.
18811881
if (auto var = dyn_cast<VarDecl>(value)) {
1882+
// Ignore interpolation variables.
1883+
if (var->getBaseName() == ctx.Id_dollarInterpolation)
1884+
return false;
1885+
18821886
auto parent = mutableLocalVarParent[declRefExpr];
18831887

18841888
// If the variable is immutable, it's fine so long as it involves
@@ -2034,7 +2038,7 @@ namespace {
20342038
if (auto autoclosure = dyn_cast<AutoClosureExpr>(dc)) {
20352039
switch (autoclosure->getThunkKind()) {
20362040
case AutoClosureExpr::Kind::AsyncLet:
2037-
return diag::actor_isolated_from_async_let;
2041+
return diag::actor_isolated_from_spawn_let;
20382042

20392043
case AutoClosureExpr::Kind::DoubleCurryThunk:
20402044
case AutoClosureExpr::Kind::SingleCurryThunk:

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,7 @@ namespace {
15371537
UNINTERESTING_ATTR(ActorIndependent)
15381538
UNINTERESTING_ATTR(GlobalActor)
15391539
UNINTERESTING_ATTR(Async)
1540+
UNINTERESTING_ATTR(Spawn)
15401541
UNINTERESTING_ATTR(Sendable)
15411542

15421543
UNINTERESTING_ATTR(AtRethrows)

lib/Sema/TypeCheckEffects.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ class EffectsHandlingWalker : public ASTWalker {
418418
if (auto ic = dyn_cast<IfConfigDecl>(D)) {
419419
recurse = asImpl().checkIfConfig(ic);
420420
} else if (auto patternBinding = dyn_cast<PatternBindingDecl>(D)) {
421-
if (patternBinding->isAsyncLet())
421+
if (patternBinding->isSpawnLet())
422422
recurse = asImpl().checkAsyncLet(patternBinding);
423423
} else {
424424
recurse = ShouldNotRecurse;
@@ -1689,7 +1689,7 @@ class Context {
16891689
bool suggestTryFixIt = reasonKind == PotentialEffectReason::Kind::Apply;
16901690

16911691
if (reasonKind == PotentialEffectReason::Kind::AsyncLet) {
1692-
message = diag::throwing_async_let_without_try;
1692+
message = diag::throwing_spawn_let_without_try;
16931693

16941694
} else if (reasonKind == PotentialEffectReason::Kind::PropertyAccess) {
16951695
message = diag::throwing_prop_access_without_try;
@@ -1922,20 +1922,20 @@ class Context {
19221922

19231923
if (auto declRef = dyn_cast<DeclRefExpr>(e)) {
19241924
if (auto var = dyn_cast<VarDecl>(declRef->getDecl())) {
1925-
if (var->isAsyncLet()) {
1925+
if (var->isSpawnLet()) {
19261926
Diags.diagnose(
1927-
e->getLoc(), diag::async_let_in_illegal_context,
1927+
e->getLoc(), diag::spawn_let_in_illegal_context,
19281928
var->getName(), static_cast<unsigned>(getKind()));
19291929
return;
19301930
}
19311931
}
19321932
}
19331933
} else if (auto patternBinding = dyn_cast_or_null<PatternBindingDecl>(
19341934
node.dyn_cast<Decl *>())) {
1935-
if (patternBinding->isAsyncLet()) {
1935+
if (patternBinding->isSpawnLet()) {
19361936
auto var = patternBinding->getAnchoringVarDecl(0);
19371937
Diags.diagnose(
1938-
e->getLoc(), diag::async_let_in_illegal_context,
1938+
e->getLoc(), diag::spawn_let_in_illegal_context,
19391939
var->getName(), static_cast<unsigned>(getKind()));
19401940
return;
19411941
}
@@ -2529,7 +2529,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
25292529
// "Async let" declarations are treated as an asynchronous call
25302530
// (to the underlying task's "get"). If the initializer was throwing,
25312531
// then the access is also treated as throwing.
2532-
if (var->isAsyncLet()) {
2532+
if (var->isSpawnLet()) {
25332533
// If the initializer could throw, we will have a 'try' in the
25342534
// application of its autoclosure.
25352535
bool throws = false;
@@ -2830,9 +2830,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28302830
case PotentialEffectReason::Kind::AsyncLet:
28312831
if (auto declR = dyn_cast<DeclRefExpr>(&diag.expr)) {
28322832
if (auto var = dyn_cast<VarDecl>(declR->getDecl())) {
2833-
if (var->isAsyncLet()) {
2833+
if (var->isSpawnLet()) {
28342834
Ctx.Diags.diagnose(declR->getLoc(),
2835-
diag::async_let_without_await,
2835+
diag::spawn_let_without_await,
28362836
var->getName());
28372837
continue;
28382838
}
@@ -2861,7 +2861,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
28612861
break;
28622862
case AutoClosureExpr::Kind::AsyncLet:
28632863
Ctx.Diags.diagnose(diag.expr.getStartLoc(),
2864-
diag::async_call_without_await_in_async_let);
2864+
diag::async_call_without_await_in_spawn_let);
28652865
break;
28662866
case AutoClosureExpr::Kind::SingleCurryThunk:
28672867
case AutoClosureExpr::Kind::DoubleCurryThunk:

test/Concurrency/await_typo_correction.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ func async() throws { }
2121
// expected-error@+1 {{found 'async' in expression; did you mean 'await'?}}{{13-18=await}}
2222
let _ = async anotherAsyncFunc()
2323

24-
// Don't emit a diagnostic here
24+
// Don't emit a diagnostic here related to 'await'
2525
async let foo = anotherAsyncFunc()
26+
// expected-warning@-1{{'async let' is now 'spawn let'}}{{5-10=spawn}}
2627
let _ = await foo
2728

2829
// I question the name choice, but it's valid

0 commit comments

Comments
 (0)