Skip to content

Commit 5d8174d

Browse files
committed
[Concurrency] Introduce spawn let
1 parent d448a77 commit 5d8174d

File tree

14 files changed

+74
-48
lines changed

14 files changed

+74
-48
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: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

include/swift/Sema/ConstraintSystem.h

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

44354435
/// Builds a type-erased return expression that can be used in dynamic
44364436
/// 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
}
@@ -5861,8 +5861,8 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
58615861
return true;
58625862
}
58635863

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

58685868
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
@@ -1144,7 +1144,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,
11441144

11451145
// If this is an async let, create a child task to compute the initializer
11461146
// value.
1147-
if (PBD->isAsyncLet()) {
1147+
if (PBD->isSpawnLet()) {
11481148
// Look through the implicit await (if present), try (if present), and
11491149
// call to reach the autoclosure that computes the value.
11501150
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
@@ -8283,7 +8283,7 @@ static Expr *wrapAsyncLetInitializer(
82838283
ASTContext &ctx = dc->getASTContext();
82848284
Expr *autoclosureExpr = cs.buildAutoClosureExpr(
82858285
initializer, closureType, /*isDefaultWrappedValue=*/false,
8286-
/*isAsyncLetWrapper=*/true);
8286+
/*isSpawnLetWrapper=*/true);
82878287

82888288
// Call the autoclosure so that the AST types line up. SILGen will ignore the
82898289
// actual calls and translate them into a different mechanism.
@@ -8384,7 +8384,7 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
83848384
// For an async let, wrap the initializer appropriately to make it a child
83858385
// task.
83868386
if (auto patternBinding = target.getInitializationPatternBindingDecl()) {
8387-
if (patternBinding->isAsyncLet()) {
8387+
if (patternBinding->isSpawnLet()) {
83888388
resultTarget.setExpr(
83898389
wrapAsyncLetInitializer(
83908390
cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,7 +2508,7 @@ FunctionType::ExtInfo ConstraintSystem::closureEffects(ClosureExpr *expr) {
25082508
bool walkToDeclPre(Decl *decl) override {
25092509
// Do not walk into function or type declarations.
25102510
if (auto *patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
2511-
if (patternBinding->isAsyncLet())
2511+
if (patternBinding->isSpawnLet())
25122512
FoundAsync = true;
25132513

25142514
return true;
@@ -4972,7 +4972,7 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion,
49724972
Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
49734973
FunctionType *closureType,
49744974
bool isDefaultWrappedValue,
4975-
bool isAsyncLetWrapper) {
4975+
bool isSpawnLetWrapper) {
49764976
auto &Context = DC->getASTContext();
49774977
bool isInDefaultArgumentContext = false;
49784978
if (auto *init = dyn_cast<Initializer>(DC)) {
@@ -4994,7 +4994,7 @@ Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
49944994

49954995
closure->setParameterList(ParameterList::createEmpty(Context));
49964996

4997-
if (isAsyncLetWrapper)
4997+
if (isSpawnLetWrapper)
49984998
closure->setThunkKind(AutoClosureExpr::Kind::AsyncLet);
49994999

50005000
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);
@@ -5500,6 +5502,20 @@ void AttributeChecker::visitGlobalActorAttr(GlobalActorAttr *attr) {
55005502
}
55015503

55025504
void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) {
5505+
if (isa<VarDecl>(D)) {
5506+
D->getASTContext().Diags.diagnose(
5507+
attr->getLocation(), diag::async_let_is_spawn_let)
5508+
.fixItReplace(attr->getRange(), "spawn");
5509+
5510+
visitAsyncOrSpawnAttr(attr);
5511+
}
5512+
}
5513+
5514+
void AttributeChecker::visitSpawnAttr(SpawnAttr *attr) {
5515+
visitAsyncOrSpawnAttr(attr);
5516+
}
5517+
5518+
void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) {
55035519
auto var = dyn_cast<VarDecl>(D);
55045520
if (!var)
55055521
return;
@@ -5510,7 +5526,7 @@ void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) {
55105526

55115527
// "Async" modifier can only be applied to local declarations.
55125528
if (!patternBinding->getDeclContext()->isLocalContext()) {
5513-
diagnoseAndRemoveAttr(attr, diag::async_let_not_local);
5529+
diagnoseAndRemoveAttr(attr, diag::spawn_let_not_local);
55145530
return;
55155531
}
55165532

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

55395555
// Async can only be used on an "async let".
55405556
if (!isLet && !diagnosedVar) {
5541-
diagnose(patternBinding->getLoc(), diag::async_not_let)
5557+
diagnose(patternBinding->getLoc(), diag::spawn_not_let)
55425558
.fixItReplace(patternBinding->getLoc(), "let");
55435559
diagnosedVar = true;
55445560
}
55455561

55465562
// Each pattern entry must have an initializer expression.
55475563
if (patternBinding->getEqualLoc(index).isInvalid()) {
5548-
diagnose(pattern->getLoc(), diag::async_let_not_initialized);
5564+
diagnose(pattern->getLoc(), diag::spawn_let_not_initialized);
55495565
attr->setInvalid();
55505566
return;
55515567
}

0 commit comments

Comments
 (0)