Skip to content

[6.0] Infer protocol isolation from its inherited protocols. #73579

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
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
42 changes: 42 additions & 0 deletions lib/Sema/TypeCheckConcurrency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4543,6 +4543,37 @@ getIsolationFromConformances(NominalTypeDecl *nominal) {
return foundIsolation;
}

/// Compute the isolation of a protocol
static std::optional<ActorIsolation>
getIsolationFromInheritedProtocols(ProtocolDecl *protocol) {
std::optional<ActorIsolation> foundIsolation;
for (auto inherited : protocol->getInheritedProtocols()) {
switch (auto protoIsolation = getActorIsolation(inherited)) {
case ActorIsolation::ActorInstance:
case ActorIsolation::Unspecified:
case ActorIsolation::Nonisolated:
case ActorIsolation::NonisolatedUnsafe:
break;

case ActorIsolation::Erased:
llvm_unreachable("protocol cannot have erased isolation");

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

if (*foundIsolation != protoIsolation)
return std::nullopt;

break;
}
}

return foundIsolation;
}

/// Compute the isolation of a nominal type from the property wrappers on
/// any stored properties.
static std::optional<ActorIsolation>
Expand Down Expand Up @@ -5231,6 +5262,17 @@ ActorIsolation ActorIsolationRequest::evaluate(
if (auto inferred = inferredIsolation(*conformanceIsolation))
return inferred;

// For a protocol, inherit isolation from the directly-inherited
// protocols.
if (ctx.LangOpts.hasFeature(Feature::GlobalActorIsolatedTypesUsability)) {
if (auto proto = dyn_cast<ProtocolDecl>(nominal)) {
if (auto protoIsolation = getIsolationFromInheritedProtocols(proto)) {
if (auto inferred = inferredIsolation(*protoIsolation))
return inferred;
}
}
}

// Before Swift 6: If the declaration is a nominal type and any property
// wrappers on its stored properties require isolation, use that.
if (auto wrapperIsolation = getIsolationFromWrappers(nominal)) {
Expand Down
26 changes: 26 additions & 0 deletions test/Concurrency/global_actor_inference_swift6.swift
Original file line number Diff line number Diff line change
Expand Up @@ -154,3 +154,29 @@ class C {
struct S: InferMainActor {
@Wrapper var value: C // okay, 'S' is isolated to 'MainActor'
}

protocol InferMainActorInherited: InferMainActor {
func f() // expected-note{{mark the protocol requirement 'f()' 'async' to allow actor-isolated conformances}}
func g()
}

@SomeGlobalActor
protocol InferSomeGlobalActor { }

protocol InferenceConflict: InferMainActorInherited, InferSomeGlobalActor { }

struct S2: InferMainActorInherited {
func f() { } // okay, 'f' is MainActor isolated, as is the requirement
@MainActor func g() { } // okay for the same reasons, but more explicitly
}

@SomeGlobalActor
struct S3: InferenceConflict {
nonisolated func g() { }
}

extension S3 {
func f() { }
// expected-error@-1{{global actor 'SomeGlobalActor'-isolated instance method 'f()' cannot be used to satisfy main actor-isolated protocol requirement}}
//expected-note@-2{{add 'nonisolated' to 'f()' to make this instance method not isolated to the actor}}
}