Skip to content

Commit 07f4bfd

Browse files
committed
[SILOptimizer] Prevent devirtualization of call to distributed witness requirements
This is a narrow fix, we are going to work on fixing this properly and allowing both devirtualization and specialization for distributed requirement witnesses. Anything that uses an ad-hoc serialization requirement scheme cannot be devirtualized because that would result in loss of ad-hoc conformance in new substitution map. Resolves: #79318 Resolves: rdar://146101172 (cherry picked from commit 0415b40)
1 parent c581445 commit 07f4bfd

File tree

2 files changed

+155
-0
lines changed

2 files changed

+155
-0
lines changed

lib/SILOptimizer/Utils/Devirtualize.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,24 @@ bool swift::canDevirtualizeClassMethod(FullApplySite applySite, ClassDecl *cd,
810810
return false;
811811
}
812812

813+
// A narrow fix for https://github.com/swiftlang/swift/issues/79318
814+
// to make sure that uses of distributed requirement witnesses are
815+
// not devirtualized because that results in a loss of the ad-hoc
816+
// requirement infomation in the re-created substitution map.
817+
//
818+
// We have a similar check in `canSpecializeFunction` which presents
819+
// specialization for exactly the same reason.
820+
//
821+
// TODO: A better way to fix this would be to record the ad-hoc conformance
822+
// requirement in `RequirementEnvironment` and adjust IRGen to handle it.
823+
if (f->hasLocation()) {
824+
if (auto *funcDecl =
825+
dyn_cast_or_null<FuncDecl>(f->getLocation().getAsDeclContext())) {
826+
if (funcDecl->isDistributedWitnessWithAdHocSerializationRequirement())
827+
return false;
828+
}
829+
}
830+
813831
return true;
814832
}
815833

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend -emit-ir -swift-version 6 -O -I %t %s
3+
// RUN: %target-swift-frontend -emit-sil -swift-version 6 -O -I %t %s | %FileCheck %s
4+
5+
// REQUIRES: concurrency
6+
// REQUIRES: distributed
7+
8+
import Distributed
9+
10+
// NOTE: None of the ad-hoc protocol requirement implementations
11+
12+
public protocol Transferable: Sendable {}
13+
14+
// NOT final on purpose
15+
public class TheSpecificResultHandlerWhichIsANonFinalClass: DistributedTargetInvocationResultHandler {
16+
public typealias SerializationRequirement = Transferable
17+
18+
public func onReturn<Success>(value: Success) async throws where Success: Transferable {
19+
}
20+
21+
public func onReturnVoid() async throws {
22+
fatalError()
23+
}
24+
25+
public func onThrow<Err>(error: Err) async throws where Err : Error {
26+
fatalError()
27+
}
28+
}
29+
30+
// NOT final on purpose
31+
public class FakeInvocationDecoder: DistributedTargetInvocationDecoder {
32+
public typealias SerializationRequirement = Transferable
33+
34+
public func decodeGenericSubstitutions() throws -> [Any.Type] {
35+
[]
36+
}
37+
38+
public func decodeNextArgument<Argument: SerializationRequirement>() throws -> Argument {
39+
fatalError()
40+
}
41+
42+
public func decodeErrorType() throws -> Any.Type? {
43+
nil
44+
}
45+
46+
public func decodeReturnType() throws -> Any.Type? {
47+
nil
48+
}
49+
}
50+
51+
// NOT final on purpose
52+
public class FakeInvocationEncoder : DistributedTargetInvocationEncoder {
53+
public typealias SerializationRequirement = Transferable
54+
55+
public func recordArgument<Value: SerializationRequirement>(
56+
_ argument: RemoteCallArgument<Value>) throws {
57+
}
58+
59+
public func recordGenericSubstitution<T>(_ type: T.Type) throws {
60+
}
61+
62+
public func recordErrorType<E: Error>(_ type: E.Type) throws {
63+
}
64+
65+
public func recordReturnType<R: SerializationRequirement>(_ type: R.Type) throws {
66+
}
67+
68+
public func doneRecording() throws {
69+
}
70+
}
71+
72+
// NOT final on purpose
73+
public class NotFinalActorSystemForAdHocRequirementTest: DistributedActorSystem, @unchecked Sendable {
74+
public typealias ActorID = String
75+
public typealias InvocationEncoder = FakeInvocationEncoder
76+
public typealias InvocationDecoder = FakeInvocationDecoder
77+
public typealias SerializationRequirement = Transferable
78+
public typealias ResultHandler = TheSpecificResultHandlerWhichIsANonFinalClass
79+
80+
public init() {}
81+
82+
public func resolve<Act>(id: ActorID, as actorType: Act.Type)
83+
throws -> Act? where Act: DistributedActor {
84+
fatalError()
85+
}
86+
87+
public func assignID<Act>(_ actorType: Act.Type) -> ActorID
88+
where Act: DistributedActor {
89+
fatalError()
90+
}
91+
92+
public func actorReady<Act>(_ actor: Act) where Act: DistributedActor, Act.ID == ActorID {
93+
fatalError()
94+
}
95+
96+
public func resignID(_ id: ActorID) {
97+
fatalError()
98+
}
99+
100+
public func makeInvocationEncoder() -> InvocationEncoder {
101+
fatalError()
102+
}
103+
104+
public func remoteCall<Act, Err, Res>(
105+
on actor: Act,
106+
target: RemoteCallTarget,
107+
invocation: inout InvocationEncoder,
108+
throwing errorType: Err.Type,
109+
returning returnType: Res.Type
110+
) async throws -> Res
111+
where Act: DistributedActor,
112+
Act.ID == ActorID,
113+
Err: Error,
114+
Res: SerializationRequirement {
115+
fatalError()
116+
}
117+
118+
public func remoteCallVoid<Act, Err>(
119+
on actor: Act,
120+
target: RemoteCallTarget,
121+
invocation: inout InvocationEncoder,
122+
throwing errorType: Err.Type
123+
) async throws
124+
where Act: DistributedActor,
125+
Act.ID == ActorID,
126+
Err: Error {
127+
fatalError()
128+
}
129+
}
130+
131+
// FIXME: This call should be devirtualized but it cannot be done at the moment due to issues with ad-hoc serialization requirement.
132+
133+
// CHECK-LABEL: sil shared [transparent] [thunk] @$s52distributed_actor_adhoc_requirements_optimized_build42NotFinalActorSystemForAdHocRequirementTestC11Distributed0piJ0AadEP10remoteCall2on6target10invocation8throwing9returningqd_1_qd___AD06RemoteR6TargetV17InvocationEncoderQzzqd_0_mqd_1_mtYaKAD0pI0Rd__s5ErrorRd_0_2IDQyd__0I2IDRtzr1_lFTW
134+
// CHECK: bb0(%0 : $*τ_0_2, %1 : $τ_0_0, %2 : $*RemoteCallTarget, %3 : $*FakeInvocationEncoder, %4 : $@thick τ_0_1.Type, %5 : $@thick τ_0_2.Type, %6 : $*NotFinalActorSystemForAdHocRequirementTest):
135+
// CHECK-NEXT: [[DIST_IMPL:%.*]] = load %6
136+
// CHECK-NEXT: [[REMOTE_CALL_WITNESS:%.*]] = class_method [[DIST_IMPL]] : $NotFinalActorSystemForAdHocRequirementTest, #NotFinalActorSystemForAdHocRequirementTest.remoteCall
137+
// CHECK-NEXT: try_apply [[REMOTE_CALL_WITNESS]]<τ_0_0, τ_0_1, τ_0_2>(%0, %1, %2, %3, %4, %5, [[DIST_IMPL]])

0 commit comments

Comments
 (0)