Skip to content

Commit 360fe60

Browse files
committed
[Actor inference] Infer global actors on all value members of a type.
Rather than restricting inference of global actors to instance members, as we do for actor types, infer global actors for all value members, which also includes static variables and methods, initializers, and destructors.
1 parent a9d5f60 commit 360fe60

File tree

2 files changed

+88
-10
lines changed

2 files changed

+88
-10
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2508,6 +2508,61 @@ static bool isAsyncHandler(ValueDecl *value) {
25082508
return false;
25092509
}
25102510

2511+
namespace {
2512+
2513+
/// Describes how actor isolation is propagated to a member, if at all.
2514+
enum class MemberIsolationPropagation {
2515+
GlobalActor,
2516+
AnyIsolation
2517+
};
2518+
2519+
}
2520+
/// Determine how the given member can receive its isolation from its type
2521+
/// context.
2522+
static Optional<MemberIsolationPropagation> getMemberIsolationPropagation(
2523+
const ValueDecl *value) {
2524+
if (!value->getDeclContext()->isTypeContext())
2525+
return None;
2526+
2527+
switch (value->getKind()) {
2528+
case DeclKind::Import:
2529+
case DeclKind::Extension:
2530+
case DeclKind::TopLevelCode:
2531+
case DeclKind::InfixOperator:
2532+
case DeclKind::PrefixOperator:
2533+
case DeclKind::PostfixOperator:
2534+
case DeclKind::IfConfig:
2535+
case DeclKind::PoundDiagnostic:
2536+
case DeclKind::PrecedenceGroup:
2537+
case DeclKind::MissingMember:
2538+
case DeclKind::Class:
2539+
case DeclKind::Enum:
2540+
case DeclKind::Protocol:
2541+
case DeclKind::Struct:
2542+
case DeclKind::TypeAlias:
2543+
case DeclKind::GenericTypeParam:
2544+
case DeclKind::AssociatedType:
2545+
case DeclKind::OpaqueType:
2546+
case DeclKind::Param:
2547+
case DeclKind::Module:
2548+
return None;
2549+
2550+
case DeclKind::PatternBinding:
2551+
case DeclKind::EnumCase:
2552+
case DeclKind::EnumElement:
2553+
case DeclKind::Constructor:
2554+
case DeclKind::Destructor:
2555+
return MemberIsolationPropagation::GlobalActor;
2556+
2557+
case DeclKind::Func:
2558+
case DeclKind::Accessor:
2559+
case DeclKind::Subscript:
2560+
case DeclKind::Var:
2561+
return value->isInstanceMember() ? MemberIsolationPropagation::AnyIsolation
2562+
: MemberIsolationPropagation::GlobalActor;
2563+
}
2564+
}
2565+
25112566
ActorIsolation ActorIsolationRequest::evaluate(
25122567
Evaluator &evaluator, ValueDecl *value) const {
25132568
// If this declaration has one of the actor isolation attributes, report
@@ -2537,12 +2592,16 @@ ActorIsolation ActorIsolationRequest::evaluate(
25372592
}
25382593

25392594
// Function used when returning an inferred isolation.
2540-
auto inferredIsolation = [&](ActorIsolation inferred) {
2595+
auto inferredIsolation = [&](
2596+
ActorIsolation inferred, bool onlyGlobal = false) {
25412597
// Add an implicit attribute to capture the actor isolation that was
25422598
// inferred, so that (e.g.) it will be printed and serialized.
25432599
ASTContext &ctx = value->getASTContext();
25442600
switch (inferred) {
25452601
case ActorIsolation::Independent:
2602+
if (onlyGlobal)
2603+
return ActorIsolation::forUnspecified();
2604+
25462605
value->getAttrs().add(new (ctx) ActorIndependentAttr(
25472606
ActorIndependentKind::Safe, /*IsImplicit=*/true));
25482607
break;
@@ -2560,6 +2619,9 @@ ActorIsolation ActorIsolationRequest::evaluate(
25602619

25612620
case ActorIsolation::ActorInstance:
25622621
case ActorIsolation::Unspecified:
2622+
if (onlyGlobal)
2623+
return ActorIsolation::forUnspecified();
2624+
25632625
// Nothing to do.
25642626
break;
25652627
}
@@ -2661,21 +2723,25 @@ ActorIsolation ActorIsolationRequest::evaluate(
26612723
}
26622724
}
26632725

2664-
// Instance members infer isolation from their context.
2665-
if (value->isInstanceMember()) {
2726+
// Infer isolation for a member.
2727+
if (auto memberPropagation = getMemberIsolationPropagation(value)) {
2728+
// If were only allowed to propagate global actors, do so.
2729+
bool onlyGlobal =
2730+
*memberPropagation == MemberIsolationPropagation::GlobalActor;
2731+
26662732
// If the declaration is in an extension that has one of the isolation
26672733
// attributes, use that.
26682734
if (auto ext = dyn_cast<ExtensionDecl>(value->getDeclContext())) {
26692735
if (auto isolationFromAttr = getIsolationFromAttributes(ext)) {
2670-
return inferredIsolation(*isolationFromAttr);
2736+
return inferredIsolation(*isolationFromAttr, onlyGlobal);
26712737
}
26722738
}
26732739

26742740
// If the declaration is in a nominal type (or extension thereof) that
26752741
// has isolation, use that.
26762742
if (auto selfTypeDecl = value->getDeclContext()->getSelfNominalTypeDecl()) {
26772743
if (auto selfTypeIsolation = getActorIsolation(selfTypeDecl))
2678-
return inferredIsolation(selfTypeIsolation);
2744+
return inferredIsolation(selfTypeIsolation, onlyGlobal);
26792745
}
26802746
}
26812747

test/Concurrency/actor_isolation.swift

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -647,14 +647,24 @@ class SomeClassWithInits {
647647
var mutableState: Int = 17
648648
var otherMutableState: Int
649649

650-
init() {
650+
static var shared = SomeClassWithInits() // expected-note{{static property declared here}}
651+
652+
init() { // expected-note{{calls to initializer 'init()' from outside of its actor context are implicitly asynchronous}}
651653
self.mutableState = 42
652654
self.otherMutableState = 17
653655

654-
self.isolated() // expected-error{{'isolated()' isolated to global actor 'MainActor' can not be referenced from this synchronous context}}
656+
self.isolated()
657+
}
658+
659+
deinit {
660+
print(SomeClassWithInits.shared) // okay, we're actor-isolated
655661
}
656662

657-
func isolated() { } // expected-note{{calls to instance method 'isolated()' from outside of its actor context are implicitly asynchronous}}
663+
func isolated() { }
664+
665+
static func staticIsolated() { // expected-note{{calls to static method 'staticIsolated()' from outside of its actor context are implicitly asynchronous}}
666+
_ = SomeClassWithInits.shared
667+
}
658668

659669
func hasDetached() {
660670
Task.runDetached {
@@ -668,8 +678,10 @@ class SomeClassWithInits {
668678
}
669679
}
670680

671-
func outsideSomeClassWithInits() {
672-
_ = SomeClassWithInits() // okay, initializer is not isolated
681+
func outsideSomeClassWithInits() { // expected-note 3 {{add '@MainActor' to make global function 'outsideSomeClassWithInits()' part of global actor 'MainActor'}}
682+
_ = SomeClassWithInits() // expected-error{{initializer 'init()' isolated to global actor 'MainActor' can not be referenced from this synchronous context}}
683+
_ = SomeClassWithInits.shared // expected-error{{static property 'shared' isolated to global actor 'MainActor' can not be referenced from this synchronous context}}
684+
SomeClassWithInits.staticIsolated() // expected-error{{static method 'staticIsolated()' isolated to global actor 'MainActor' can not be referenced from this synchronous context}}
673685
}
674686

675687
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)