Skip to content

Infer a type's actor isolation from the property wrappers it uses. #36771

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
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
61 changes: 59 additions & 2 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2539,6 +2539,56 @@ static Optional<ActorIsolation> getIsolationFromConformances(
return foundIsolation;
}

/// Compute the isolation of a nominal type from the property wrappers on
/// any stored properties.
static Optional<ActorIsolation> getIsolationFromWrappers(
NominalTypeDecl *nominal) {
if (!isa<StructDecl>(nominal) && !isa<ClassDecl>(nominal))
return None;

if (!nominal->getParentSourceFile())
return None;

Optional<ActorIsolation> foundIsolation;
for (auto member : nominal->getMembers()) {
auto var = dyn_cast<VarDecl>(member);
if (!var || !var->isInstanceMember())
continue;

auto info = var->getAttachedPropertyWrapperTypeInfo(0);
if (!info)
continue;

auto isolation = getActorIsolation(info.valueVar);

// Inconsistent wrappedValue/projectedValue isolation disables inference.
if (info.projectedValueVar &&
getActorIsolation(info.projectedValueVar) != isolation)
continue;

switch (isolation) {
case ActorIsolation::ActorInstance:
case ActorIsolation::Unspecified:
case ActorIsolation::Independent:
break;

case ActorIsolation::GlobalActor:
case ActorIsolation::GlobalActorUnsafe:
if (!foundIsolation) {
foundIsolation = isolation;
continue;
}

if (*foundIsolation != isolation)
return None;

break;
}
}

return foundIsolation;
}

// Check whether a declaration is an asynchronous handler.
static bool isAsyncHandler(ValueDecl *value) {
if (auto func = dyn_cast<AbstractFunctionDecl>(value)) {
Expand Down Expand Up @@ -2763,12 +2813,19 @@ ActorIsolation ActorIsolationRequest::evaluate(
}
}

// If the declaration is a nominal type and any of the protocols to which
// it directly conforms is isolated to a global actor, use that.
if (auto nominal = dyn_cast<NominalTypeDecl>(value)) {
// If the declaration is a nominal type and any of the protocols to which
// it directly conforms is isolated to a global actor, use that.
if (auto conformanceIsolation = getIsolationFromConformances(nominal))
if (auto inferred = inferredIsolation(*conformanceIsolation))
return inferred;

// If the declaration is a nominal type and any property wrappers on
// its stored properties require isolation, use that.
if (auto wrapperIsolation = getIsolationFromWrappers(nominal)) {
if (auto inferred = inferredIsolation(*wrapperIsolation))
return inferred;
}
}
}

Expand Down
28 changes: 28 additions & 0 deletions test/Concurrency/global_actor_inference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,34 @@ actor ActorWithWrapper {
}
}

@propertyWrapper
struct WrapperOnSomeGlobalActor<Wrapped> {
@actorIndependent(unsafe) private var stored: Wrapped

nonisolated init(wrappedValue: Wrapped) {
stored = wrappedValue
}

@SomeGlobalActor var wrappedValue: Wrapped {
get { stored }
set { stored = newValue }
}
}

struct InferredFromPropertyWrapper {
@WrapperOnSomeGlobalActor var value = 17

func test() -> Int { // expected-note{{calls to instance method 'test()' from outside of its actor context are implicitly asynchronous}}
value
}
}

func testInferredFromWrapper(x: InferredFromPropertyWrapper) { // expected-note{{add '@SomeGlobalActor' to make global function 'testInferredFromWrapper(x:)' part of global actor 'SomeGlobalActor'}}
_ = x.test() // expected-error{{instance method 'test()' isolated to global actor 'SomeGlobalActor' can not be referenced from this synchronous context}}
}



// ----------------------------------------------------------------------
// Unsafe global actors
// ----------------------------------------------------------------------
Expand Down