Skip to content

Commit 65b07e9

Browse files
committed
[SILGen] Emit expected executor preconditions for synchronous isolated @preconcurrency witness thunks
For `@preconcurrency` conformance witness thunks replace hop to executor with a precondition to make sure that the thunk is always called in the expected context.
1 parent 4513084 commit 65b07e9

File tree

5 files changed

+398
-5
lines changed

5 files changed

+398
-5
lines changed

lib/SILGen/SILGenFunction.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,12 +946,16 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
946946
///
947947
/// This is used for both concrete witness thunks and default witness
948948
/// thunks.
949+
///
950+
/// \param isPreconcurrency If the conformance is marked as `@preconcurrency`
951+
/// instead of a hop (when entering isolation) emit a dynamic check to make
952+
/// sure that witness has been unsed in the expected context.
949953
void emitProtocolWitness(AbstractionPattern reqtOrigTy,
950954
CanAnyFunctionType reqtSubstTy,
951955
SILDeclRef requirement, SubstitutionMap reqtSubs,
952956
SILDeclRef witness, SubstitutionMap witnessSubs,
953957
IsFreeFunctionWitness_t isFree,
954-
bool isSelfConformance,
958+
bool isSelfConformance, bool isPreconcurrency,
955959
llvm::Optional<ActorIsolation> enterIsolation);
956960

957961
/// Generates subscript arguments for keypath. This function handles lowering

lib/SILGen/SILGenPoly.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6861,7 +6861,8 @@ void SILGenFunction::emitProtocolWitness(
68616861
AbstractionPattern reqtOrigTy, CanAnyFunctionType reqtSubstTy,
68626862
SILDeclRef requirement, SubstitutionMap reqtSubs, SILDeclRef witness,
68636863
SubstitutionMap witnessSubs, IsFreeFunctionWitness_t isFree,
6864-
bool isSelfConformance, llvm::Optional<ActorIsolation> enterIsolation) {
6864+
bool isSelfConformance, bool isPreconcurrency,
6865+
llvm::Optional<ActorIsolation> enterIsolation) {
68656866
// FIXME: Disable checks that the protocol witness carries debug info.
68666867
// Should we carry debug info for witnesses?
68676868
F.setBare(IsBare);
@@ -6905,7 +6906,13 @@ void SILGenFunction::emitProtocolWitness(
69056906
actorSelf = actorSelfVal;
69066907
}
69076908

6908-
emitHopToTargetActor(loc, enterIsolation, actorSelf);
6909+
if (!F.isAsync()) {
6910+
assert(isPreconcurrency);
6911+
auto executor = emitExecutor(loc, *enterIsolation, actorSelf);
6912+
emitPreconditionCheckExpectedExecutor(loc, *executor);
6913+
} else {
6914+
emitHopToTargetActor(loc, enterIsolation, actorSelf);
6915+
}
69096916
}
69106917

69116918
// Get the type of the witness.

lib/SILGen/SILGenType.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -834,9 +834,21 @@ SILFunction *SILGenModule::emitProtocolWitness(
834834
// archetypes of the witness thunk generic environment.
835835
auto witnessSubs = witness.getSubstitutions();
836836

837+
// If the conformance is marked as `@preconcurrency` instead of
838+
// emitting a hop to the executor (when needed) emit a dynamic check
839+
// to make sure that witness has been unsed in the expected context.
840+
bool isPreconcurrency = false;
841+
if (conformance.isConcrete()) {
842+
if (auto *C =
843+
dyn_cast<NormalProtocolConformance>(conformance.getConcrete()))
844+
isPreconcurrency = C->isPreconcurrency();
845+
}
846+
837847
SGF.emitProtocolWitness(AbstractionPattern(reqtOrigTy), reqtSubstTy,
838848
requirement, reqtSubMap, witnessRef,
839-
witnessSubs, isFree, /*isSelfConformance*/ false,
849+
witnessSubs, isFree,
850+
/*isSelfConformance*/ false,
851+
isPreconcurrency,
840852
witness.getEnterIsolation());
841853

842854
emitLazyConformancesForFunction(f);
@@ -910,7 +922,8 @@ static SILFunction *emitSelfConformanceWitness(SILGenModule &SGM,
910922

911923
SGF.emitProtocolWitness(AbstractionPattern(reqtOrigTy), reqtSubstTy,
912924
requirement, reqtSubs, requirement, witnessSubs,
913-
isFree, /*isSelfConformance*/ true, llvm::None);
925+
isFree, /*isSelfConformance*/ true,
926+
/*isPreconcurrency*/ false, llvm::None);
914927

915928
SGM.emitLazyConformancesForFunction(f);
916929

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// RUN: %empty-directory(%t/src)
2+
// RUN: split-file %s %t/src
3+
4+
// RUN: %target-build-swift %t/src/Interface.swift -swift-version 5 -emit-module -emit-library \
5+
// RUN: -enable-library-evolution \
6+
// RUN: -module-name Interface \
7+
// RUN: -o %t/%target-library-name(Interface) \
8+
// RUN: -emit-module-interface-path %t/Interface.swiftinterface
9+
10+
// RUN: %target-build-swift %t/src/Types.swift -swift-version 5 -emit-module -emit-library -enable-library-evolution -module-name Types -o %t/%target-library-name(Types) \
11+
// RUN: -I %t -L %t -l Interface \
12+
// RUN: -emit-module-interface-path %t/Types.swiftinterface \
13+
// RUN: -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances
14+
15+
// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash1.swift -o %t/crash1.out
16+
// RUN: %target-codesign %t/crash1.out
17+
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash1.out 2>&1 | %FileCheck %t/src/Crash1.swift
18+
19+
// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash2.swift -o %t/crash2.out
20+
// RUN: %target-codesign %t/crash2.out
21+
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash2.out 2>&1 | %FileCheck %t/src/Crash2.swift
22+
23+
// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash3.swift -o %t/crash3.out
24+
// RUN: %target-codesign %t/crash3.out
25+
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash3.out 2>&1 | %FileCheck %t/src/Crash3.swift
26+
27+
// RUN: %target-build-swift -Xfrontend -enable-experimental-feature -Xfrontend PreconcurrencyConformances -I %t -L %t -l Types %t/src/Crash4.swift -o %t/crash4.out
28+
// RUN: %target-codesign %t/crash4.out
29+
// RUN: not --crash env SWIFT_UNEXPECTED_EXECUTOR_LOG_LEVEL=2 %target-run %t/crash4.out 2>&1 | %FileCheck %t/src/Crash4.swift
30+
31+
// REQUIRES: asserts
32+
// REQUIRES: concurrency
33+
// REQUIRES: executable_test
34+
35+
//--- Interface.swift
36+
public protocol P {
37+
init()
38+
39+
var prop: [String] { get set }
40+
func test() -> Int
41+
}
42+
43+
//--- Types.swift
44+
import Interface
45+
46+
public func runTest<T: P>(_ type: T.Type) async -> Int {
47+
let v = type.init()
48+
return v.test()
49+
}
50+
51+
public func runAccessors<T: P>(_ type: T.Type) async -> [String] {
52+
var v = type.init()
53+
v.prop = ["a", "b", "c"]
54+
return v.prop
55+
}
56+
57+
public final class Test : @preconcurrency P {
58+
@MainActor public var prop: [String] = []
59+
@MainActor public func test() -> Int { 42 }
60+
61+
public init() {}
62+
}
63+
64+
public actor ActorTest {
65+
var x: Int = 0
66+
67+
public init() {}
68+
}
69+
70+
extension ActorTest : @preconcurrency P {
71+
public var prop: [String] {
72+
get { [] }
73+
set { }
74+
}
75+
76+
public func test() -> Int { x }
77+
}
78+
79+
//--- Crash1.swift
80+
import Types
81+
print(await runTest(Test.self))
82+
// CHECK: error: data race detected: @MainActor function at Types/Types.swift:16 was not called on the main thread
83+
84+
//--- Crash2.swift
85+
import Types
86+
print(await runAccessors(Test.self))
87+
// CHECK: error: data race detected: @MainActor function at Types/Types.swift:15 was not called on the main thread
88+
89+
//--- Crash3.swift
90+
import Types
91+
print(await runTest(ActorTest.self))
92+
// CHECK: error: data race detected: actor-isolated function at Types/Types.swift:33 was not called on the same actor
93+
94+
//--- Crash4.swift
95+
import Types
96+
print(await runAccessors(ActorTest.self))
97+
// CHECK: error: data race detected: actor-isolated function at Types/Types.swift:30 was not called on the same actor

0 commit comments

Comments
 (0)