Skip to content

Reinstate "async let", with "spawn let" as an alias. #37306

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1865,7 +1865,7 @@ class PatternBindingDecl final : public Decl,
bool isComputingPatternBindingEntry(const VarDecl *vd) const;

/// Is this an "async let" declaration?
bool isSpawnLet() const;
bool isAsyncLet() const;

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

/// Is this an "async let" property?
bool isSpawnLet() const;
bool isAsyncLet() const;

Introducer getIntroducer() const {
return Introducer(Bits.VarDecl.Introducer);
Expand Down
41 changes: 19 additions & 22 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -4246,8 +4246,8 @@ ERROR(throwing_interpolation_without_try,none,
"interpolation can throw but is not marked with 'try'", ())
ERROR(throwing_call_without_try,none,
"call can throw but is not marked with 'try'", ())
ERROR(throwing_spawn_let_without_try,none,
"reading 'spawn let' can throw but is not marked with 'try'", ())
ERROR(throwing_async_let_without_try,none,
"reading 'async let' can throw but is not marked with 'try'", ())
ERROR(throwing_prop_access_without_try,none,
"property access can throw but is not marked with 'try'", ())
ERROR(throwing_subscript_access_without_try,none,
Expand Down Expand Up @@ -4276,8 +4276,8 @@ NOTE(async_access_without_await,none,

NOTE(async_call_without_await_in_autoclosure,none,
"call is 'async' in an autoclosure argument", ())
NOTE(async_call_without_await_in_spawn_let,none,
"call is 'async' in an 'spawn let' initializer", ())
NOTE(async_call_without_await_in_async_let,none,
"call is 'async' in an 'async let' initializer", ())

WARNING(no_async_in_await,none,
"no 'async' operations occur within 'await' expression", ())
Expand All @@ -4290,7 +4290,7 @@ ERROR(await_in_illegal_context,none,
"%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",
(unsigned))
ERROR(async_in_nonasync_function,none,
"%select{'async'|'async' call|'await'|'spawn let'|'async' property access|'async' subscript access}0 in "
"%select{'async'|'async' call|'await'|'async let'|'async' property access|'async' subscript access}0 in "
"%select{a function|an autoclosure}1 that does not support concurrency",
(unsigned, bool))
NOTE(note_add_async_to_function,none,
Expand All @@ -4317,21 +4317,18 @@ NOTE(protocol_witness_async_conflict,none,
ERROR(async_autoclosure_nonasync_function,none,
"'async' autoclosure parameter in a non-'async' function", ())

WARNING(async_let_is_spawn_let,none,
"'async let' is now 'spawn let'", ())

ERROR(spawn_not_let,none,
"'spawn' can only be used with 'let' declarations", ())
ERROR(spawn_let_not_local,none,
"'spawn let' can only be used on local declarations", ())
ERROR(spawn_let_not_initialized,none,
"'spawn let' binding requires an initializer expression", ())
ERROR(spawn_let_no_variables,none,
"'spawn let' requires at least one named variable", ())
NOTE(spawn_let_without_await,none,
"reference to spawn let %0 is 'async'", (DeclName))
ERROR(spawn_let_in_illegal_context,none,
"spawn let %0 cannot be referenced in "
ERROR(async_not_let,none,
"'async' can only be used with 'let' declarations", ())
ERROR(async_let_not_local,none,
"'async let' can only be used on local declarations", ())
ERROR(async_let_not_initialized,none,
"'async let' binding requires an initializer expression", ())
ERROR(async_let_no_variables,none,
"'async let' requires at least one named variable", ())
NOTE(async_let_without_await,none,
"reference to async let %0 is 'async'", (DeclName))
ERROR(async_let_in_illegal_context,none,
"async let %0 cannot be referenced in "
"%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",
(DeclName, unsigned))

Expand Down Expand Up @@ -4428,8 +4425,8 @@ ERROR(actor_isolated_from_concurrent_closure,none,
ERROR(actor_isolated_from_concurrent_function,none,
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from a concurrent function",
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(actor_isolated_from_spawn_let,none,
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'spawn let' initializer",
ERROR(actor_isolated_from_async_let,none,
"actor-isolated %0 %1 cannot be %select{referenced|mutated|used 'inout'}2 from 'async let' initializer",
(DescriptiveDeclKind, DeclName, unsigned))
ERROR(actor_isolated_keypath_component,none,
"cannot form key path to actor-isolated %0 %1",
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Sema/ConstraintSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -4431,7 +4431,7 @@ class ConstraintSystem {
/// Given expression represents computed result of the closure.
Expr *buildAutoClosureExpr(Expr *expr, FunctionType *closureType,
bool isDefaultWrappedValue = false,
bool isSpawnLetWrapper = false);
bool isAsyncLetWrapper = false);

/// Builds a type-erased return expression that can be used in dynamic
/// replacement.
Expand Down
6 changes: 3 additions & 3 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1581,9 +1581,9 @@ StaticSpellingKind PatternBindingDecl::getCorrectStaticSpelling() const {
return getCorrectStaticSpellingForDecl(this);
}

bool PatternBindingDecl::isSpawnLet() const {
bool PatternBindingDecl::isAsyncLet() const {
if (auto var = getAnchoringVarDecl(0))
return var->isSpawnLet();
return var->isAsyncLet();

return false;
}
Expand Down Expand Up @@ -5877,7 +5877,7 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
return true;
}

bool VarDecl::isSpawnLet() const {
bool VarDecl::isAsyncLet() const {
return getAttrs().hasAttribute<AsyncAttr>() || getAttrs().hasAttribute<SpawnAttr>();
}

Expand Down
4 changes: 2 additions & 2 deletions lib/SILGen/SILGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,7 @@ class LetValueInitialization : public Initialization {
// buffer. DI will make sure it is only assigned to once.
needsTemporaryBuffer = true;
isUninitialized = true;
} else if (vd->isSpawnLet()) {
} else if (vd->isAsyncLet()) {
// If this is an async let, treat it like a let-value without an
// initializer. The initializer runs concurrently in a child task,
// and value will be initialized at the point the variable in the
Expand Down Expand Up @@ -1144,7 +1144,7 @@ void SILGenFunction::emitPatternBinding(PatternBindingDecl *PBD,

// If this is an async let, create a child task to compute the initializer
// value.
if (PBD->isSpawnLet()) {
if (PBD->isAsyncLet()) {
// Look through the implicit await (if present), try (if present), and
// call to reach the autoclosure that computes the value.
auto *init = PBD->getExecutableInit(idx);
Expand Down
2 changes: 1 addition & 1 deletion lib/SILGen/SILGenLValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2816,7 +2816,7 @@ SILGenFunction::maybeEmitValueOfLocalVarDecl(
if (It != VarLocs.end()) {
// If the variable is part of an async let, ensure that the child task
// has completed first.
if (var->isSpawnLet() && accessKind != AccessKind::Write) {
if (var->isAsyncLet() && accessKind != AccessKind::Write) {
auto patternBinding = var->getParentPatternBinding();
unsigned index = patternBinding->getPatternEntryIndexForVarDecl(var);
completeAsyncLetChildTask(patternBinding, index);
Expand Down
4 changes: 2 additions & 2 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8316,7 +8316,7 @@ static Expr *wrapAsyncLetInitializer(
ASTContext &ctx = dc->getASTContext();
Expr *autoclosureExpr = cs.buildAutoClosureExpr(
initializer, closureType, /*isDefaultWrappedValue=*/false,
/*isSpawnLetWrapper=*/true);
/*isAsyncLetWrapper=*/true);

// Call the autoclosure so that the AST types line up. SILGen will ignore the
// actual calls and translate them into a different mechanism.
Expand Down Expand Up @@ -8417,7 +8417,7 @@ static Optional<SolutionApplicationTarget> applySolutionToInitialization(
// For an async let, wrap the initializer appropriately to make it a child
// task.
if (auto patternBinding = target.getInitializationPatternBindingDecl()) {
if (patternBinding->isSpawnLet()) {
if (patternBinding->isAsyncLet()) {
resultTarget.setExpr(
wrapAsyncLetInitializer(
cs, resultTarget.getAsExpr(), resultTarget.getDeclContext()));
Expand Down
6 changes: 3 additions & 3 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4945,7 +4945,7 @@ ConstraintSystem::isConversionEphemeral(ConversionRestrictionKind conversion,
Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,
FunctionType *closureType,
bool isDefaultWrappedValue,
bool isSpawnLetWrapper) {
bool isAsyncLetWrapper) {
auto &Context = DC->getASTContext();
bool isInDefaultArgumentContext = false;
if (auto *init = dyn_cast<Initializer>(DC)) {
Expand All @@ -4967,7 +4967,7 @@ Expr *ConstraintSystem::buildAutoClosureExpr(Expr *expr,

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

if (isSpawnLetWrapper)
if (isAsyncLetWrapper)
closure->setThunkKind(AutoClosureExpr::Kind::AsyncLet);

Expr *result = closure;
Expand Down Expand Up @@ -5709,7 +5709,7 @@ ASTNode constraints::findAsyncNode(ClosureExpr *closure) {
bool walkToDeclPre(Decl *decl) override {
// Do not walk into function or type declarations.
if (auto *patternBinding = dyn_cast<PatternBindingDecl>(decl)) {
if (patternBinding->isSpawnLet())
if (patternBinding->isAsyncLet())
AsyncNode = patternBinding;

return true;
Expand Down
12 changes: 4 additions & 8 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5503,10 +5503,6 @@ void AttributeChecker::visitGlobalActorAttr(GlobalActorAttr *attr) {

void AttributeChecker::visitAsyncAttr(AsyncAttr *attr) {
if (isa<VarDecl>(D)) {
D->getASTContext().Diags.diagnose(
attr->getLocation(), diag::async_let_is_spawn_let)
.fixItReplace(attr->getRange(), "spawn");

visitAsyncOrSpawnAttr(attr);
}
}
Expand All @@ -5526,7 +5522,7 @@ void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) {

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

Expand All @@ -5547,21 +5543,21 @@ void AttributeChecker::visitAsyncOrSpawnAttr(DeclAttribute *attr) {
// Each entry must bind at least one named variable, so that there is
// something to "await".
if (!foundAnyVariable) {
diagnose(pattern->getLoc(), diag::spawn_let_no_variables);
diagnose(pattern->getLoc(), diag::async_let_no_variables);
attr->setInvalid();
return;
}

// Async can only be used on an "async let".
if (!isLet && !diagnosedVar) {
diagnose(patternBinding->getLoc(), diag::spawn_not_let)
diagnose(patternBinding->getLoc(), diag::async_not_let)
.fixItReplace(patternBinding->getLoc(), "let");
diagnosedVar = true;
}

// Each pattern entry must have an initializer expression.
if (patternBinding->getEqualLoc(index).isInvalid()) {
diagnose(pattern->getLoc(), diag::spawn_let_not_initialized);
diagnose(pattern->getLoc(), diag::async_let_not_initialized);
attr->setInvalid();
return;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2044,7 +2044,7 @@ namespace {
if (auto autoclosure = dyn_cast<AutoClosureExpr>(dc)) {
switch (autoclosure->getThunkKind()) {
case AutoClosureExpr::Kind::AsyncLet:
return diag::actor_isolated_from_spawn_let;
return diag::actor_isolated_from_async_let;

case AutoClosureExpr::Kind::DoubleCurryThunk:
case AutoClosureExpr::Kind::SingleCurryThunk:
Expand Down
20 changes: 10 additions & 10 deletions lib/Sema/TypeCheckEffects.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ class EffectsHandlingWalker : public ASTWalker {
if (auto ic = dyn_cast<IfConfigDecl>(D)) {
recurse = asImpl().checkIfConfig(ic);
} else if (auto patternBinding = dyn_cast<PatternBindingDecl>(D)) {
if (patternBinding->isSpawnLet())
if (patternBinding->isAsyncLet())
recurse = asImpl().checkAsyncLet(patternBinding);
} else {
recurse = ShouldNotRecurse;
Expand Down Expand Up @@ -1689,7 +1689,7 @@ class Context {
bool suggestTryFixIt = reasonKind == PotentialEffectReason::Kind::Apply;

if (reasonKind == PotentialEffectReason::Kind::AsyncLet) {
message = diag::throwing_spawn_let_without_try;
message = diag::throwing_async_let_without_try;

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

if (auto declRef = dyn_cast<DeclRefExpr>(e)) {
if (auto var = dyn_cast<VarDecl>(declRef->getDecl())) {
if (var->isSpawnLet()) {
if (var->isAsyncLet()) {
Diags.diagnose(
e->getLoc(), diag::spawn_let_in_illegal_context,
e->getLoc(), diag::async_let_in_illegal_context,
var->getName(), static_cast<unsigned>(getKind()));
return;
}
}
}
} else if (auto patternBinding = dyn_cast_or_null<PatternBindingDecl>(
node.dyn_cast<Decl *>())) {
if (patternBinding->isSpawnLet()) {
if (patternBinding->isAsyncLet()) {
auto var = patternBinding->getAnchoringVarDecl(0);
Diags.diagnose(
e->getLoc(), diag::spawn_let_in_illegal_context,
e->getLoc(), diag::async_let_in_illegal_context,
var->getName(), static_cast<unsigned>(getKind()));
return;
}
Expand Down Expand Up @@ -2529,7 +2529,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
// "Async let" declarations are treated as an asynchronous call
// (to the underlying task's "get"). If the initializer was throwing,
// then the access is also treated as throwing.
if (var->isSpawnLet()) {
if (var->isAsyncLet()) {
// If the initializer could throw, we will have a 'try' in the
// application of its autoclosure.
bool throws = false;
Expand Down Expand Up @@ -2831,9 +2831,9 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
case PotentialEffectReason::Kind::AsyncLet:
if (auto declR = dyn_cast<DeclRefExpr>(&diag.expr)) {
if (auto var = dyn_cast<VarDecl>(declR->getDecl())) {
if (var->isSpawnLet()) {
if (var->isAsyncLet()) {
Ctx.Diags.diagnose(declR->getLoc(),
diag::spawn_let_without_await,
diag::async_let_without_await,
var->getName());
continue;
}
Expand Down Expand Up @@ -2862,7 +2862,7 @@ class CheckEffectsCoverage : public EffectsHandlingWalker<CheckEffectsCoverage>
break;
case AutoClosureExpr::Kind::AsyncLet:
Ctx.Diags.diagnose(diag.expr.getStartLoc(),
diag::async_call_without_await_in_spawn_let);
diag::async_call_without_await_in_async_let);
break;
case AutoClosureExpr::Kind::SingleCurryThunk:
case AutoClosureExpr::Kind::DoubleCurryThunk:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,10 @@ OVERRIDE_TASK(task_create_group_future_common, AsyncTaskAndContext, , , ,
(JobFlags flags, TaskGroup *group,
const Metadata *futureResultType,
FutureAsyncSignature::FunctionType *function,
void *closureContext, bool isSpawnLetTask,
void *closureContext, bool isAsyncLetTask,
size_t initialContextSize),
(flags, group, futureResultType, function, closureContext,
isSpawnLetTask, initialContextSize))
isAsyncLetTask, initialContextSize))

OVERRIDE_TASK(task_future_wait, void, SWIFT_EXPORT_FROM(swift_Concurrency),
SWIFT_CC(swiftasync), swift::,
Expand Down
Loading