From b0b23a070bfb18474293d43062df109e57186e0c Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Fri, 20 Jun 2025 14:26:42 -0400 Subject: [PATCH] Sema: Expand isolated conformance inference to consider conformances to inherited protocols Otherwise, if PDerived inherits from P and P's requirements are witnessed by @MainActor-isolated members, we fail to infer @MainActor isolation on the conformance to PDerived. - Fixes https://github.com/swiftlang/swift/issues/82222. - Fixes rdar://153219831. --- lib/Sema/TypeCheckConcurrency.cpp | 22 ++++++++++++++----- .../Inputs/isolated_conformance_other.swift | 5 +++++ .../isolated_conformance_inference.swift | 4 ++++ ...ormance_inference_with_default_actor.swift | 9 ++++++++ 4 files changed, 35 insertions(+), 5 deletions(-) create mode 100644 test/Concurrency/Inputs/isolated_conformance_other.swift create mode 100644 test/Concurrency/isolated_conformance_inference_with_default_actor.swift diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index b65c9a5f36d94..34aed98b49ca1 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -8155,16 +8155,28 @@ ActorIsolation swift::inferConformanceIsolation( return nominalIsolation; } - bool anyIsolatedWitness = false; auto protocol = conformance->getProtocol(); - for (auto requirement : protocol->getMembers()) { - if (isa(requirement)) + + // Also check the value witnesses of each implied conformance to every + // inherited protocol, recursively. + for (auto req : protocol->getRequirementSignature().getRequirements()) { + if (req.getKind() != RequirementKind::Conformance || + !req.getFirstType()->isEqual(ctx.TheSelfType)) continue; - auto valueReq = dyn_cast(requirement); - if (!valueReq) + auto *assocConf = conformance->getAssociatedConformance( + req.getFirstType(), req.getProtocolDecl()).getConcrete(); + auto isolation = assocConf->getIsolation(); + if (isolation.isGlobalActor()) + return isolation; + } + + bool anyIsolatedWitness = false; + for (auto requirement : protocol->getProtocolRequirements()) { + if (isa(requirement)) continue; + auto valueReq = cast(requirement); auto witness = conformance->getWitnessDecl(valueReq); if (!witness) continue; diff --git a/test/Concurrency/Inputs/isolated_conformance_other.swift b/test/Concurrency/Inputs/isolated_conformance_other.swift new file mode 100644 index 0000000000000..8b4fa2e88e697 --- /dev/null +++ b/test/Concurrency/Inputs/isolated_conformance_other.swift @@ -0,0 +1,5 @@ +public protocol P { + func f() +} + +public protocol PDerived: P {} diff --git a/test/Concurrency/isolated_conformance_inference.swift b/test/Concurrency/isolated_conformance_inference.swift index d3481c2c651c5..35bf78603a3ba 100644 --- a/test/Concurrency/isolated_conformance_inference.swift +++ b/test/Concurrency/isolated_conformance_inference.swift @@ -92,3 +92,7 @@ class InferMeDefaults { var someGlobalActorState: any P2.Type = DifferingConformances.self // expected-error{{global actor 'SomeGlobalActor'-isolated default value in a main actor-isolated context}} var bothState: any (P & P2).Type = DifferingConformances.self // expected-error{{default argument cannot be both main actor-isolated and global actor 'SomeGlobalActor'-isolated}} } + +protocol PDerived: P {} + +@MainActor struct ImpliedConformanceInference: PDerived { func f() {} } diff --git a/test/Concurrency/isolated_conformance_inference_with_default_actor.swift b/test/Concurrency/isolated_conformance_inference_with_default_actor.swift new file mode 100644 index 0000000000000..57a86a58978b7 --- /dev/null +++ b/test/Concurrency/isolated_conformance_inference_with_default_actor.swift @@ -0,0 +1,9 @@ +// RUN: %empty-directory(%t) +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/isolated_conformance_other.swiftmodule %S/Inputs/isolated_conformance_other.swift -swift-version 6 +// RUN: %target-typecheck-verify-swift -I %t -swift-version 6 -enable-upcoming-feature InferIsolatedConformances -default-isolation MainActor -swift-version 6 +// REQUIRES: swift_feature_InferIsolatedConformances + +import isolated_conformance_other + +struct S1: P { func f() {} } +struct S2: PDerived { func f() {} }