Skip to content

Commit 760a6fa

Browse files
authored
Merge pull request #82427 from gottesmm/pr-9d8b2e21000560a9c3a3b0143c3fb3b22a0ca75a
[rbi] Remove code that caused us to misidentify certain captured parameters as sending.
2 parents 164d3ae + cff1c77 commit 760a6fa

File tree

6 files changed

+147
-47
lines changed

6 files changed

+147
-47
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -538,34 +538,23 @@ static bool isAsyncLetBeginPartialApply(PartialApplyInst *pai) {
538538

539539
/// Returns true if this is a function argument that is able to be sent in the
540540
/// body of our function.
541+
///
542+
/// Needs to stay in sync with SILIsolationInfo::get(SILArgument *).
541543
static bool canFunctionArgumentBeSent(SILFunctionArgument *arg) {
542544
// Indirect out parameters can never be sent.
543545
if (arg->isIndirectResult() || arg->isIndirectErrorResult())
544546
return false;
545547

546-
// If we have a function argument that is closure captured by a Sendable
547-
// closure, allow for the argument to be sent.
548-
//
549-
// DISCUSSION: The reason that we do this is that in the case of us
550-
// having an actual Sendable closure there are two cases we can see:
551-
//
552-
// 1. If we have an actual Sendable closure, the AST will emit an
553-
// earlier error saying that we are capturing a non-Sendable value in a
554-
// Sendable closure. So we want to squelch the error that we would emit
555-
// otherwise. This only occurs when we are not in swift-6 mode since in
556-
// swift-6 mode we will error on the earlier error... but in the case of
557-
// us not being in swift 6 mode lets not emit extra errors.
558-
//
559-
// 2. If we have an async-let based Sendable closure, we want to allow
560-
// for the argument to be sent in the async let's statement and
561-
// not emit an error.
562-
//
563-
// TODO: Once the async let refactoring change this will no longer be needed
564-
// since closure captures will have sending parameters and be
565-
// non-Sendable.
566-
if (arg->isClosureCapture() &&
567-
arg->getFunction()->getLoweredFunctionType()->isSendable())
568-
return true;
548+
// If we have a closure capture...
549+
if (arg->isClosureCapture()) {
550+
// And that closure capture is from an async let, treat it as sending. This
551+
// is because we allow for disconnected values to be sent into async let
552+
// closures.
553+
if (auto declRef = arg->getFunction()->getDeclRef();
554+
declRef && declRef.isAsyncLetClosure) {
555+
return true;
556+
}
557+
}
569558

570559
// Otherwise, we only allow for the argument to be sent if it is explicitly
571560
// marked as a 'sending' parameter.

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,16 +1020,36 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10201020
if (fArg->isSending())
10211021
return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/);
10221022

1023+
// Check if we have a closure captured parameter that is nonisolated(unsafe)
1024+
// at its original declaration sign. In such a case, we want to propagate that
1025+
// bit.
1026+
bool isClosureCapturedNonisolatedUnsafe = [&]() -> bool {
1027+
if (!fArg->isClosureCapture())
1028+
return false;
1029+
auto *decl = fArg->getDecl();
1030+
if (!decl)
1031+
return false;
1032+
auto *attr = decl->getAttrs().getAttribute<NonisolatedAttr>();
1033+
if (!attr)
1034+
return false;
1035+
return attr->isUnsafe();
1036+
}();
1037+
10231038
// If we have a closure capture that is not an indirect result or indirect
10241039
// result error, we want to treat it as sending so that we properly handle
10251040
// async lets.
10261041
//
10271042
// This pattern should only come up with async lets. See comment in
1028-
// isTransferrableFunctionArgument.
1043+
// canFunctionArgumentBeSent in RegionAnalysis.cpp. This code needs to stay in
1044+
// sync with that code.
10291045
if (!fArg->isIndirectResult() && !fArg->isIndirectErrorResult() &&
1030-
fArg->isClosureCapture() &&
1031-
fArg->getFunction()->getLoweredFunctionType()->isSendable())
1032-
return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/);
1046+
fArg->isClosureCapture()) {
1047+
if (auto declRef = arg->getFunction()->getDeclRef();
1048+
declRef && declRef.isAsyncLetClosure) {
1049+
return SILIsolationInfo::getDisconnected(
1050+
isClosureCapturedNonisolatedUnsafe);
1051+
}
1052+
}
10331053

10341054
// Before we do anything further, see if we have an isolated parameter. This
10351055
// handles isolated self and specifically marked isolated.
@@ -1040,12 +1060,20 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10401060
if (auto funcIsolation = fArg->getFunction()->getActorIsolation();
10411061
funcIsolation && funcIsolation->isCallerIsolationInheriting()) {
10421062
return SILIsolationInfo::getTaskIsolated(fArg)
1043-
.withNonisolatedNonsendingTaskIsolated(true);
1063+
.withNonisolatedNonsendingTaskIsolated(true)
1064+
.withUnsafeNonIsolated(isClosureCapturedNonisolatedUnsafe);
10441065
}
10451066

10461067
auto astType = isolatedArg->getType().getASTType();
10471068
if (astType->lookThroughAllOptionalTypes()->getAnyActor()) {
1048-
return SILIsolationInfo::getActorInstanceIsolated(fArg, isolatedArg);
1069+
if (auto isolation =
1070+
SILIsolationInfo::getActorInstanceIsolated(fArg, isolatedArg)) {
1071+
return isolation.withUnsafeNonIsolated(
1072+
isClosureCapturedNonisolatedUnsafe);
1073+
}
1074+
// If we had an isolated parameter that was an actor type, but we did not
1075+
// find any isolation for the actor instance... return {}.
1076+
return {};
10491077
}
10501078
}
10511079

@@ -1087,12 +1115,14 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10871115
}
10881116

10891117
// Otherwise, if we do not have an isolated argument and are not in an
1090-
// allocator, then we might be isolated via global isolation.
1118+
// allocator, then we might be isolated via global isolation or in a global
1119+
// actor isolated initializer.
10911120
if (auto functionIsolation = fArg->getFunction()->getActorIsolation()) {
10921121
if (functionIsolation->isActorIsolated()) {
10931122
if (functionIsolation->isGlobalActor()) {
10941123
return SILIsolationInfo::getGlobalActorIsolated(
1095-
fArg, functionIsolation->getGlobalActor());
1124+
fArg, functionIsolation->getGlobalActor())
1125+
.withUnsafeNonIsolated(isClosureCapturedNonisolatedUnsafe);
10961126
}
10971127

10981128
return SILIsolationInfo::getActorInstanceIsolated(
@@ -1101,7 +1131,8 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
11011131
}
11021132
}
11031133

1104-
return SILIsolationInfo::getTaskIsolated(fArg);
1134+
return SILIsolationInfo::getTaskIsolated(fArg).withUnsafeNonIsolated(
1135+
isClosureCapturedNonisolatedUnsafe);
11051136
}
11061137

11071138
/// Infer isolation region from the set of protocol conformances.

test/Concurrency/sendable_checking.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,13 +463,15 @@ func preconcurrencyContext(_: @escaping @Sendable () -> Void) {}
463463
struct DowngradeForPreconcurrency {
464464
func capture(completion: @escaping @MainActor () -> Void) {
465465
preconcurrencyContext {
466-
Task {
467-
completion()
466+
Task { // expected-tns-warning {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}}
467+
completion() // expected-tns-note {{closure captures 'completion' which is accessible to code in the current task}}
468468
// expected-warning@-1 {{capture of 'completion' with non-Sendable type '@MainActor () -> Void' in a '@Sendable' closure}}
469469
// expected-warning@-2 {{capture of 'completion' with non-Sendable type '@MainActor () -> Void' in an isolated closure}}
470470
// expected-note@-3 2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
471471
// expected-warning@-4 {{expression is 'async' but is not marked with 'await'; this is an error in the Swift 6 language mode}}
472472
// expected-note@-5 {{calls to parameter 'completion' from outside of its actor context are implicitly asynchronous}}
473+
// expected-complete-and-tns-warning @-7 {{passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure}}
474+
// expected-complete-and-tns-note @-7 {{closure captures 'completion' which is accessible to code in the current task}}
473475
}
474476
}
475477
}

test/Concurrency/transfernonsendable.swift

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,13 +1671,12 @@ extension MyActor {
16711671
_ = self
16721672
_ = sc
16731673

1674-
Task { // expected-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}}
1675-
// expected-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to initializer 'init(name:priority:operation:)' risks causing races in between local and caller code}}
1676-
_ = sc
1674+
Task { // expected-warning {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}}
1675+
_ = sc // expected-note {{closure captures 'self'-isolated 'sc'}}
16771676
}
16781677

1679-
Task { // expected-note {{access can happen concurrently}}
1680-
_ = sc
1678+
Task { // expected-warning {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}}
1679+
_ = sc // expected-note {{closure captures 'self'-isolated 'sc'}}
16811680
}
16821681
}
16831682
}
@@ -2001,3 +2000,22 @@ nonisolated(nonsending) func testCallNonisolatedNonsending(_ x: NonSendableKlass
20012000
// expected-ni-note @-1 {{sending nonisolated(nonsending) task-isolated 'x' to nonisolated global function 'useValueAsyncConcurrent' risks causing data races between nonisolated and nonisolated(nonsending) task-isolated uses}}
20022001
// expected-ni-ns-note @-2 {{sending task-isolated 'x' to @concurrent global function 'useValueAsyncConcurrent' risks causing data races between @concurrent and task-isolated uses}}
20032002
}
2003+
2004+
func avoidThinkingClosureParameterIsSending() {
2005+
@MainActor
2006+
final class Foo {
2007+
let value = NonSendableKlass()
2008+
2009+
func perform() {
2010+
Task { [value] in
2011+
await value.asyncCall() // expected-ni-warning {{sending 'value' risks causing data races}}
2012+
// expected-ni-note @-1 {{sending main actor-isolated 'value' to nonisolated instance method 'asyncCall()' risks causing data races between nonisolated and main actor-isolated uses}}
2013+
}
2014+
2015+
Task {
2016+
await value.asyncCall() // expected-ni-warning {{sending 'self.value' risks causing data races}}
2017+
// expected-ni-note @-1 {{sending main actor-isolated 'self.value' to nonisolated instance method 'asyncCall()' risks causing data races between nonisolated and main actor-isolated uses}}
2018+
}
2019+
}
2020+
}
2021+
}

test/Concurrency/transfernonsendable_nonisolatedunsafe.swift

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -823,9 +823,9 @@ actor ActorContainingSendableStruct {
823823
}
824824

825825

826-
////////////////////
827-
// MARK: Closures //
828-
////////////////////
826+
////////////////////////////
827+
// MARK: Sending Closures //
828+
////////////////////////////
829829

830830
func closureTests() async {
831831
func sendingClosure(_ x: sending () -> ()) {
@@ -1043,3 +1043,64 @@ func closureTests() async {
10431043
sendingClosure(y) // expected-note {{access can happen concurrently}}
10441044
}
10451045
}
1046+
1047+
///////////////////////////////////////
1048+
// MARK: Closure Capture Propagation //
1049+
///////////////////////////////////////
1050+
//
1051+
// DISCUSSION: For tests that involve closure captures with nonisolated(unsafe)
1052+
// and suppressing errors in the closure itself. Tests where the suppressed
1053+
// error is outside the closure itself are in other places in the file.
1054+
1055+
enum NonisolatedUnsafeCapture {
1056+
func testSimple() {
1057+
nonisolated(unsafe) let x = NonSendableKlass()
1058+
let _ = {
1059+
await transferToMainDirect(x)
1060+
print(x)
1061+
}
1062+
}
1063+
1064+
func testAsyncLet() async {
1065+
nonisolated(unsafe) let x = NonSendableKlass()
1066+
async let y: () = {
1067+
await transferToMainDirect(x)
1068+
await transferToMainDirect(x)
1069+
}()
1070+
_ = await y
1071+
}
1072+
1073+
func testIsolatedParamViaActor() async {
1074+
actor A {
1075+
let y = NonSendableKlass()
1076+
func test() {
1077+
nonisolated(unsafe) let x = y
1078+
_ = {
1079+
await transferToMainDirect(x)
1080+
}
1081+
}
1082+
}
1083+
}
1084+
1085+
func testExplicitIsolatedParam() async {
1086+
nonisolated(unsafe) let x = NonSendableKlass()
1087+
_ = { (y: isolated CustomActorInstance) in
1088+
await transferToMainDirect(x)
1089+
}
1090+
}
1091+
1092+
func testIsolatedParamNonisolatedNonSending() async {
1093+
nonisolated(unsafe) let x = NonSendableKlass()
1094+
let _: nonisolated(nonsending) () async -> () = {
1095+
await transferToMainDirect(x)
1096+
await transferToMainDirect(x)
1097+
}
1098+
}
1099+
1100+
func testGlobalActorIsolated() async {
1101+
nonisolated(unsafe) let x = NonSendableKlass()
1102+
_ = { @MainActor in
1103+
await transferToCustom(x)
1104+
}
1105+
}
1106+
}

test/Concurrency/transfernonsendable_typed_errors.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,12 @@ extension MyActor {
6161
_ = self
6262
_ = sc
6363

64-
Task { // expected-error {{sending value of non-Sendable type '() async -> ()' risks causing data races}}
65-
// expected-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to initializer 'init(name:priority:operation:)' risks causing races in between local and caller code}}
66-
_ = sc
64+
Task { // expected-error {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}}
65+
_ = sc // expected-note {{closure captures 'self'-isolated 'sc'}}
6766
}
6867

69-
Task { // expected-note {{access can happen concurrently}}
70-
_ = sc
68+
Task { // expected-error {{passing closure as a 'sending' parameter risks causing data races between 'self'-isolated code and concurrent execution of the closure}}
69+
_ = sc // expected-note {{closure captures 'self'-isolated 'sc'}}
7170
}
7271
}
7372
}

0 commit comments

Comments
 (0)