From d73b907d6653aaace07444d693ed761f5fb98394 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 19 Sep 2025 09:01:23 +0900 Subject: [PATCH 1/3] [Distributed] Avoid redundant conformance to DistributedActor in interface files Forgetting to handle DistributedActor next to Actor strikes again: Actors are special that their conformance is implicit and needs not be printed in swift interface files. an actor's Actor conformance was filtered out, however the same logic needs to be applied to DistributedActor, as otherwise there can be redundant conformance errors when validating module interface files. Resolves rdar://160252579 --- lib/AST/ProtocolConformance.cpp | 2 +- lib/Frontend/ModuleInterfaceSupport.cpp | 12 ++++++--- ...distributed_no_redundant_conformance.swift | 26 +++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 test/ModuleInterface/distributed_no_redundant_conformance.swift diff --git a/lib/AST/ProtocolConformance.cpp b/lib/AST/ProtocolConformance.cpp index 9fdff392a9ee0..f6d072a88478a 100644 --- a/lib/AST/ProtocolConformance.cpp +++ b/lib/AST/ProtocolConformance.cpp @@ -1267,7 +1267,7 @@ void NominalTypeDecl::prepareConformanceTable() const { } } - // Actor classes conform to the actor protocol. + // Actor classes conform to the appropriate actor protocol. if (auto classDecl = dyn_cast(mutableThis)) { if (classDecl->isDistributedActor()) { addSynthesized(ctx.getProtocol(KnownProtocolKind::DistributedActor)); diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index 229e7f031e343..f77ec884dcb96 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -688,10 +688,13 @@ class InheritedProtocolCollector { if (!printOptions.shouldPrint(nominal)) return; - /// is this nominal specifically an 'actor'? + /// is this nominal specifically an 'actor' or 'distributed actor'? bool actorClass = false; - if (auto klass = dyn_cast(nominal)) + bool distributedActorClass = false; + if (auto klass = dyn_cast(nominal)) { actorClass = klass->isActor(); + distributedActorClass = klass->isDistributedActor(); + } SmallPtrSet handledProtocols; @@ -727,7 +730,10 @@ class InheritedProtocolCollector { // it is only valid to conform to Actor on an 'actor' decl, // not extensions of that 'actor'. if (actorClass && - inherited->isSpecificProtocol(KnownProtocolKind::Actor)) + inherited->isSpecificProtocol(KnownProtocolKind::Actor)) + return TypeWalker::Action::SkipNode; + if (distributedActorClass && + inherited->isSpecificProtocol(KnownProtocolKind::DistributedActor)) return TypeWalker::Action::SkipNode; // Do not synthesize an extension to print a conformance to an diff --git a/test/ModuleInterface/distributed_no_redundant_conformance.swift b/test/ModuleInterface/distributed_no_redundant_conformance.swift new file mode 100644 index 0000000000000..e3b05a4a2f376 --- /dev/null +++ b/test/ModuleInterface/distributed_no_redundant_conformance.swift @@ -0,0 +1,26 @@ +// RUN: %empty-directory(%t) + +// RUN: %target-swift-emit-module-interface(%t/TestResilient.swiftinterface) %s -module-name TestResilient +// RUN: %target-swift-typecheck-module-from-interface(%t/TestResilient.swiftinterface) -module-name TestResilient +// RUN: %FileCheck %s --dump-input=always < %t/TestResilient.swiftinterface + +// RUN: %target-swift-frontend -compile-module-from-interface -swift-version 5 %t/TestResilient.swiftinterface -o %t/TestResilient.swiftmodule +// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules -swift-version 5 -emit-module-interface-path - %t/TestResilient.swiftmodule -module-name TestResilient | %FileCheck %s + +import Distributed + +// Note that tat while an actor is implicitly conforming to Actor, we don't need to print this in interfaces +// as it would cause 'redundant conformance of ... to (Distributed)Actor' issues. + +public actor Example {} + +// CHECK-NOT: extension TestResilient.Example : _Concurrency.Actor {} + +public distributed actor DistributedExample { + public typealias ActorSystem = LocalTestingDistributedActorSystem + distributed func example() {} +} + +// CHECK: distributed public actor DistributedExample { + +// CHECK-NOT: extension TestResilient.DistributedExample : Distributed.DistributedActor {} \ No newline at end of file From 1ea4e914e35f192ea882fd25fcd993bf4e085ca3 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 19 Sep 2025 09:06:04 +0900 Subject: [PATCH 2/3] Simplify the check; conforming to (Distributed)Actor is not right in any case --- lib/Frontend/ModuleInterfaceSupport.cpp | 17 +++++++---------- .../distributed_no_redundant_conformance.swift | 7 ++----- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/lib/Frontend/ModuleInterfaceSupport.cpp b/lib/Frontend/ModuleInterfaceSupport.cpp index f77ec884dcb96..1f5238ccb8a01 100644 --- a/lib/Frontend/ModuleInterfaceSupport.cpp +++ b/lib/Frontend/ModuleInterfaceSupport.cpp @@ -689,11 +689,9 @@ class InheritedProtocolCollector { return; /// is this nominal specifically an 'actor' or 'distributed actor'? - bool actorClass = false; - bool distributedActorClass = false; + bool anyActorClass = false; if (auto klass = dyn_cast(nominal)) { - actorClass = klass->isActor(); - distributedActorClass = klass->isDistributedActor(); + anyActorClass = klass->isAnyActor(); } SmallPtrSet handledProtocols; @@ -729,12 +727,11 @@ class InheritedProtocolCollector { // There is a special restriction on the Actor protocol in that // it is only valid to conform to Actor on an 'actor' decl, // not extensions of that 'actor'. - if (actorClass && - inherited->isSpecificProtocol(KnownProtocolKind::Actor)) - return TypeWalker::Action::SkipNode; - if (distributedActorClass && - inherited->isSpecificProtocol(KnownProtocolKind::DistributedActor)) - return TypeWalker::Action::SkipNode; + if (anyActorClass) { + if (inherited->isSpecificProtocol(KnownProtocolKind::Actor) || + inherited->isSpecificProtocol(KnownProtocolKind::DistributedActor)) + return TypeWalker::Action::SkipNode; + } // Do not synthesize an extension to print a conformance to an // invertible protocol, as their conformances are always re-inferred diff --git a/test/ModuleInterface/distributed_no_redundant_conformance.swift b/test/ModuleInterface/distributed_no_redundant_conformance.swift index e3b05a4a2f376..88b88bc5c1f0d 100644 --- a/test/ModuleInterface/distributed_no_redundant_conformance.swift +++ b/test/ModuleInterface/distributed_no_redundant_conformance.swift @@ -2,10 +2,7 @@ // RUN: %target-swift-emit-module-interface(%t/TestResilient.swiftinterface) %s -module-name TestResilient // RUN: %target-swift-typecheck-module-from-interface(%t/TestResilient.swiftinterface) -module-name TestResilient -// RUN: %FileCheck %s --dump-input=always < %t/TestResilient.swiftinterface - -// RUN: %target-swift-frontend -compile-module-from-interface -swift-version 5 %t/TestResilient.swiftinterface -o %t/TestResilient.swiftmodule -// RUN: %target-swift-frontend -emit-module -o /dev/null -merge-modules -swift-version 5 -emit-module-interface-path - %t/TestResilient.swiftmodule -module-name TestResilient | %FileCheck %s +// RUN: %FileCheck %s < %t/TestResilient.swiftinterface import Distributed @@ -23,4 +20,4 @@ public distributed actor DistributedExample { // CHECK: distributed public actor DistributedExample { -// CHECK-NOT: extension TestResilient.DistributedExample : Distributed.DistributedActor {} \ No newline at end of file +// CHECK-NOT: extension TestResilient.DistributedExample : Distributed.DistributedActor {} From 7affd14c24d997649d42381788f5c3bbceb9a0f6 Mon Sep 17 00:00:00 2001 From: Konrad Malawski Date: Fri, 19 Sep 2025 15:44:11 +0900 Subject: [PATCH 3/3] fix assertions in other test --- test/ModuleInterface/distributed-actor.swift | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/ModuleInterface/distributed-actor.swift b/test/ModuleInterface/distributed-actor.swift index 2edb8c2efbe15..1d8c73c73096e 100644 --- a/test/ModuleInterface/distributed-actor.swift +++ b/test/ModuleInterface/distributed-actor.swift @@ -73,9 +73,6 @@ public distributed actor DAG where ActorSystem: DistributedActorSys // CHECK: } } -// CHECK-NOT: #if compiler(>=5.3) && $Actors -// CHECK: @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) -// CHECK-NEXT:extension Library.DA : Distributed.DistributedActor {} // CHECK-NOT: #if compiler(>=5.3) && $Actors // CHECK: @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) // CHECK-NEXT:extension Library.DA : Swift.Encodable {} @@ -83,10 +80,6 @@ public distributed actor DAG where ActorSystem: DistributedActorSys // CHECK: @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) // CHECK-NEXT:extension Library.DA : Swift.Decodable {} -// CHECK-NOT: #if compiler(>=5.3) && $Actors -// CHECK: @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -// CHECK-NEXT: extension Library.DAG : Distributed.DistributedActor {} - //--- Client.swift import Distributed