Skip to content

Commit 09e1c34

Browse files
authored
Merge pull request swiftlang#36014 from DougGregor/global-actor-closures
[Concurrency] Allow async closures to be annotated with global actors.
2 parents 7d531f1 + 5bb290c commit 09e1c34

File tree

6 files changed

+116
-13
lines changed

6 files changed

+116
-13
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4448,6 +4448,9 @@ ERROR(global_actor_non_unsafe_init,none,
44484448
ERROR(actor_isolation_multiple_attr,none,
44494449
"%0 %1 has multiple actor-isolation attributes ('%2' and '%3')",
44504450
(DescriptiveDeclKind, DeclName, StringRef, StringRef))
4451+
ERROR(global_actor_isolated_synchronous_closure,none,
4452+
"closure isolated to global actor %0 must be 'async'",
4453+
(Type))
44514454

44524455
ERROR(actor_isolation_override_mismatch,none,
44534456
"%0 %1 %2 has different actor isolation from %3 overridden declaration",

include/swift/AST/TypeCheckRequests.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -985,7 +985,8 @@ using CustomAttrNominalPair = std::pair<CustomAttr *, NominalTypeDecl *>;
985985
class GlobalActorAttributeRequest :
986986
public SimpleRequest<
987987
GlobalActorAttributeRequest,
988-
Optional<CustomAttrNominalPair>(Decl *),
988+
Optional<CustomAttrNominalPair>(
989+
llvm::PointerUnion<Decl *, ClosureExpr *>),
989990
RequestFlags::Cached> {
990991
public:
991992
using SimpleRequest::SimpleRequest;
@@ -995,7 +996,8 @@ class GlobalActorAttributeRequest :
995996

996997
// Evaluation.
997998
Optional<std::pair<CustomAttr *, NominalTypeDecl *>>
998-
evaluate(Evaluator &evaluator, Decl *decl) const;
999+
evaluate(
1000+
Evaluator &evaluator, llvm::PointerUnion<Decl *, ClosureExpr *>) const;
9991001

10001002
public:
10011003
// Caching

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ SWIFT_REQUEST(TypeChecker, GlobalActorInstanceRequest,
102102
VarDecl *(NominalTypeDecl *),
103103
Cached, NoLocationInfo)
104104
SWIFT_REQUEST(TypeChecker, GlobalActorAttributeRequest,
105-
Optional<CustomAttrNominalPair>(Decl *),
105+
Optional<CustomAttrNominalPair>(
106+
llvm::PointerUnion<Decl *, ClosureExpr *>),
106107
Cached, NoLocationInfo)
107108
SWIFT_REQUEST(TypeChecker, ActorIsolationRequest,
108109
ActorIsolationState(ValueDecl *),

lib/Sema/TypeCheckAttr.cpp

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5584,9 +5584,10 @@ namespace {
55845584
class ClosureAttributeChecker
55855585
: public AttributeVisitor<ClosureAttributeChecker> {
55865586
ASTContext &ctx;
5587+
ClosureExpr *closure;
55875588
public:
55885589
ClosureAttributeChecker(ClosureExpr *closure)
5589-
: ctx(closure->getASTContext()) { }
5590+
: ctx(closure->getASTContext()), closure(closure) { }
55905591

55915592
void visitDeclAttribute(DeclAttribute *attr) {
55925593
ctx.Diags.diagnose(
@@ -5599,6 +5600,29 @@ class ClosureAttributeChecker
55995600
void visitConcurrentAttr(ConcurrentAttr *attr) {
56005601
// Nothing else to check.
56015602
}
5603+
5604+
void visitCustomAttr(CustomAttr *attr) {
5605+
// Check whether this custom attribute is the global actor attribute.
5606+
auto globalActorAttr = evaluateOrDefault(
5607+
ctx.evaluator, GlobalActorAttributeRequest{closure}, None);
5608+
if (globalActorAttr && globalActorAttr->first == attr)
5609+
return;
5610+
5611+
// Otherwise, it's an error.
5612+
std::string typeName;
5613+
if (auto typeRepr = attr->getTypeRepr()) {
5614+
llvm::raw_string_ostream out(typeName);
5615+
typeRepr->print(out);
5616+
} else {
5617+
typeName = attr->getType().getString();
5618+
}
5619+
5620+
ctx.Diags.diagnose(
5621+
attr->getLocation(), diag::unsupported_closure_attr,
5622+
attr->isDeclModifier(), typeName)
5623+
.fixItRemove(attr->getRangeWithAt());
5624+
attr->setInvalid();
5625+
}
56025626
};
56035627

56045628
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -419,13 +419,26 @@ VarDecl *GlobalActorInstanceRequest::evaluate(
419419

420420
Optional<std::pair<CustomAttr *, NominalTypeDecl *>>
421421
GlobalActorAttributeRequest::evaluate(
422-
Evaluator &evaluator, Decl *decl) const {
423-
ASTContext &ctx = decl->getASTContext();
424-
auto dc = decl->getDeclContext();
422+
Evaluator &evaluator,
423+
llvm::PointerUnion<Decl *, ClosureExpr *> subject) const {
424+
DeclContext *dc;
425+
DeclAttributes *declAttrs;
426+
SourceLoc loc;
427+
if (auto decl = subject.dyn_cast<Decl *>()) {
428+
dc = decl->getDeclContext();
429+
declAttrs = &decl->getAttrs();
430+
loc = decl->getLoc();
431+
} else {
432+
auto closure = subject.get<ClosureExpr *>();
433+
dc = closure;
434+
declAttrs = &closure->getAttrs();
435+
loc = closure->getLoc();
436+
}
437+
ASTContext &ctx = dc->getASTContext();
425438
CustomAttr *globalActorAttr = nullptr;
426439
NominalTypeDecl *globalActorNominal = nullptr;
427440

428-
for (auto attr : decl->getAttrs().getAttributes<CustomAttr>()) {
441+
for (auto attr : declAttrs->getAttributes<CustomAttr>()) {
429442
auto mutableAttr = const_cast<CustomAttr *>(attr);
430443
// Figure out which nominal declaration this custom attribute refers to.
431444
auto nominal = evaluateOrDefault(ctx.evaluator,
@@ -440,10 +453,10 @@ GlobalActorAttributeRequest::evaluate(
440453
if (!nominal->isGlobalActor())
441454
continue;
442455

443-
// Only a single global actor can be applied to a given declaration.
456+
// Only a single global actor can be applied to a given entity.
444457
if (globalActorAttr) {
445-
decl->diagnose(
446-
diag::multiple_global_actors, globalActorNominal->getName(),
458+
ctx.Diags.diagnose(
459+
loc, diag::multiple_global_actors, globalActorNominal->getName(),
447460
nominal->getName());
448461
continue;
449462
}
@@ -455,8 +468,14 @@ GlobalActorAttributeRequest::evaluate(
455468
if (!globalActorAttr)
456469
return None;
457470

471+
// Closures can always have a global actor attached.
472+
if (auto closure = subject.dyn_cast<ClosureExpr *>()) {
473+
return std::make_pair(globalActorAttr, globalActorNominal);
474+
}
475+
458476
// Check that a global actor attribute makes sense on this kind of
459477
// declaration.
478+
auto decl = subject.get<Decl *>();
460479
if (auto nominal = dyn_cast<NominalTypeDecl>(decl)) {
461480
// Nominal types are okay...
462481
if (auto classDecl = dyn_cast<ClassDecl>(nominal)){
@@ -1473,8 +1492,13 @@ namespace {
14731492

14741493
auto dc = const_cast<DeclContext *>(constDC);
14751494
while (!dc->isModuleScopeContext()) {
1476-
// Look through non-escaping closures.
14771495
if (auto closure = dyn_cast<AbstractClosureExpr>(dc)) {
1496+
// If this closure has specific isolation, use it.
1497+
auto closureIsolation = getActorIsolationOfContext(dc);
1498+
if (closureIsolation != ActorIsolation::Independent)
1499+
return closureIsolation;
1500+
1501+
// Look through non-escaping closures.
14781502
if (auto type = closure->getType()) {
14791503
if (auto fnType = type->getAs<AnyFunctionType>()) {
14801504
if (fnType->isNoEscape()) {
@@ -2039,12 +2063,50 @@ namespace {
20392063
llvm_unreachable("unhandled actor isolation kind!");
20402064
}
20412065

2066+
// Attempt to resolve the global actor type of a closure.
2067+
Type resolveGlobalActorType(ClosureExpr *closure) {
2068+
auto globalActorAttr = evaluateOrDefault(
2069+
ctx.evaluator, GlobalActorAttributeRequest{closure}, None);
2070+
if (!globalActorAttr)
2071+
return Type();
2072+
2073+
Type globalActor = evaluateOrDefault(
2074+
ctx.evaluator,
2075+
CustomAttrTypeRequest{
2076+
globalActorAttr->first, closure, CustomAttrTypeKind::GlobalActor},
2077+
Type());
2078+
if (!globalActor || globalActor->hasError())
2079+
return Type();
2080+
2081+
// Actor-isolated closures must be async.
2082+
bool isAsync = false;
2083+
if (Type closureType = closure->getType()) {
2084+
if (auto closureFnType = closureType->getAs<FunctionType>())
2085+
isAsync = closureFnType->isAsync();
2086+
}
2087+
2088+
if (!isAsync) {
2089+
ctx.Diags.diagnose(
2090+
closure->getLoc(), diag::global_actor_isolated_synchronous_closure,
2091+
globalActor);
2092+
return Type();
2093+
}
2094+
2095+
return globalActor;
2096+
}
2097+
20422098
/// Determine the isolation of a particular closure.
20432099
///
20442100
/// This function assumes that enclosing closures have already had their
20452101
/// isolation checked.
20462102
ClosureActorIsolation determineClosureIsolation(
20472103
AbstractClosureExpr *closure) {
2104+
// If the closure specifies a global actor, use it.
2105+
if (auto explicitClosure = dyn_cast<ClosureExpr>(closure)) {
2106+
if (Type globalActorType = resolveGlobalActorType(explicitClosure))
2107+
return ClosureActorIsolation::forGlobalActor(globalActorType);
2108+
}
2109+
20482110
// Escaping and concurrent closures are always actor-independent.
20492111
if (isEscapingClosure(closure) || isConcurrentClosure(closure))
20502112
return ClosureActorIsolation::forIndependent();

test/Concurrency/actor_isolation.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func acceptConcurrentClosure<T>(_: @concurrent () -> T) { }
1010
func acceptEscapingClosure<T>(_: @escaping () -> T) { }
1111
func acceptEscapingClosure<T>(_: @escaping (String) -> ()) async -> T? { nil }
1212

13-
func acceptAsyncClosure<T>(_: () async -> T) { }
13+
@discardableResult func acceptAsyncClosure<T>(_: () async -> T) -> T { }
1414
func acceptEscapingAsyncClosure<T>(_: @escaping () async -> T) { }
1515
func acceptInout<T>(_: inout T) {}
1616

@@ -361,6 +361,17 @@ actor Crystal {
361361
}
362362
@asyncHandler @SomeOtherGlobalActor func goo2() { await goo1() }
363363

364+
func testGlobalActorClosures() {
365+
let _: Int = acceptAsyncClosure { @SomeGlobalActor in
366+
syncGlobalActorFunc()
367+
syncOtherGlobalActorFunc() // expected-error{{call is 'async' but is not marked with 'await'}}
368+
await syncOtherGlobalActorFunc()
369+
return 17
370+
}
371+
372+
acceptConcurrentClosure { @SomeGlobalActor in 5 } // expected-error{{closure isolated to global actor 'SomeGlobalActor' must be 'async'}}
373+
}
374+
364375
extension MyActor {
365376
@SomeGlobalActor func onGlobalActor(otherActor: MyActor) async {
366377
// Access to other functions in this actor are okay.

0 commit comments

Comments
 (0)