From 337a99d1666cc1c75e5c8e0c3a7e91ce9d60fa6f Mon Sep 17 00:00:00 2001 From: Doug Gregor Date: Fri, 10 May 2024 13:10:11 -0700 Subject: [PATCH] Infer protocol isolation from its inherited protocols. We somehow missed this inference rule. Put it behind SE-0434's `GlobalActorIsolatedTypesUsability` flag. Fixes rdar://127843050. (cherry picked from commit 90341b202ba6c25c3a20db9ca852d35f5db26868) --- lib/Sema/TypeCheckConcurrency.cpp | 42 +++++++++++++++++++ .../global_actor_inference_swift6.swift | 26 ++++++++++++ 2 files changed, 68 insertions(+) diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 88f1def1acaaf..20431ebf5f9aa 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -4543,6 +4543,37 @@ getIsolationFromConformances(NominalTypeDecl *nominal) { return foundIsolation; } +/// Compute the isolation of a protocol +static std::optional +getIsolationFromInheritedProtocols(ProtocolDecl *protocol) { + std::optional 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 @@ -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(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)) { diff --git a/test/Concurrency/global_actor_inference_swift6.swift b/test/Concurrency/global_actor_inference_swift6.swift index 32c51a56c52d2..2f908c80ca8a4 100644 --- a/test/Concurrency/global_actor_inference_swift6.swift +++ b/test/Concurrency/global_actor_inference_swift6.swift @@ -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}} +}