Skip to content

Commit 1f33aa9

Browse files
committed
Infer a type's actor isolation from the property wrappers it uses.
Fixes rdar://76252310.
1 parent 927c709 commit 1f33aa9

File tree

2 files changed

+87
-2
lines changed

2 files changed

+87
-2
lines changed

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2539,6 +2539,56 @@ static Optional<ActorIsolation> getIsolationFromConformances(
25392539
return foundIsolation;
25402540
}
25412541

2542+
/// Compute the isolation of a nominal type from the property wrappers on
2543+
/// any stored properties.
2544+
static Optional<ActorIsolation> getIsolationFromWrappers(
2545+
NominalTypeDecl *nominal) {
2546+
if (!isa<StructDecl>(nominal) && !isa<ClassDecl>(nominal))
2547+
return None;
2548+
2549+
if (!nominal->getParentSourceFile())
2550+
return None;
2551+
2552+
Optional<ActorIsolation> foundIsolation;
2553+
for (auto member : nominal->getMembers()) {
2554+
auto var = dyn_cast<VarDecl>(member);
2555+
if (!var || !var->isInstanceMember())
2556+
continue;
2557+
2558+
auto info = var->getAttachedPropertyWrapperTypeInfo(0);
2559+
if (!info)
2560+
continue;
2561+
2562+
auto isolation = getActorIsolation(info.valueVar);
2563+
2564+
// Inconsistent wrappedValue/projectedValue isolation disables inference.
2565+
if (info.projectedValueVar &&
2566+
getActorIsolation(info.projectedValueVar) != isolation)
2567+
continue;
2568+
2569+
switch (isolation) {
2570+
case ActorIsolation::ActorInstance:
2571+
case ActorIsolation::Unspecified:
2572+
case ActorIsolation::Independent:
2573+
break;
2574+
2575+
case ActorIsolation::GlobalActor:
2576+
case ActorIsolation::GlobalActorUnsafe:
2577+
if (!foundIsolation) {
2578+
foundIsolation = isolation;
2579+
continue;
2580+
}
2581+
2582+
if (*foundIsolation != isolation)
2583+
return None;
2584+
2585+
break;
2586+
}
2587+
}
2588+
2589+
return foundIsolation;
2590+
}
2591+
25422592
// Check whether a declaration is an asynchronous handler.
25432593
static bool isAsyncHandler(ValueDecl *value) {
25442594
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
@@ -2763,12 +2813,19 @@ ActorIsolation ActorIsolationRequest::evaluate(
27632813
}
27642814
}
27652815

2766-
// If the declaration is a nominal type and any of the protocols to which
2767-
// it directly conforms is isolated to a global actor, use that.
27682816
if (auto nominal = dyn_cast<NominalTypeDecl>(value)) {
2817+
// If the declaration is a nominal type and any of the protocols to which
2818+
// it directly conforms is isolated to a global actor, use that.
27692819
if (auto conformanceIsolation = getIsolationFromConformances(nominal))
27702820
if (auto inferred = inferredIsolation(*conformanceIsolation))
27712821
return inferred;
2822+
2823+
// If the declaration is a nominal type and any property wrappers on
2824+
// its stored properties require isolation, use that.
2825+
if (auto wrapperIsolation = getIsolationFromWrappers(nominal)) {
2826+
if (auto inferred = inferredIsolation(*wrapperIsolation))
2827+
return inferred;
2828+
}
27722829
}
27732830
}
27742831

test/Concurrency/global_actor_inference.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,34 @@ actor ActorWithWrapper {
370370
}
371371
}
372372

373+
@propertyWrapper
374+
struct WrapperOnSomeGlobalActor<Wrapped> {
375+
@actorIndependent(unsafe) private var stored: Wrapped
376+
377+
nonisolated init(wrappedValue: Wrapped) {
378+
stored = wrappedValue
379+
}
380+
381+
@SomeGlobalActor var wrappedValue: Wrapped {
382+
get { stored }
383+
set { stored = newValue }
384+
}
385+
}
386+
387+
struct InferredFromPropertyWrapper {
388+
@WrapperOnSomeGlobalActor var value = 17
389+
390+
func test() -> Int { // expected-note{{calls to instance method 'test()' from outside of its actor context are implicitly asynchronous}}
391+
value
392+
}
393+
}
394+
395+
func testInferredFromWrapper(x: InferredFromPropertyWrapper) { // expected-note{{add '@SomeGlobalActor' to make global function 'testInferredFromWrapper(x:)' part of global actor 'SomeGlobalActor'}}
396+
_ = x.test() // expected-error{{instance method 'test()' isolated to global actor 'SomeGlobalActor' can not be referenced from this synchronous context}}
397+
}
398+
399+
400+
373401
// ----------------------------------------------------------------------
374402
// Unsafe global actors
375403
// ----------------------------------------------------------------------

0 commit comments

Comments
 (0)