Skip to content

Commit 3cfcc17

Browse files
committed
[region-isolation] Do not allow for a disconnected value passed as an explicitly sent parameter to be reused.
This only occurs specifically for async nonisolated functions with an isolated parameter that passes a disconnected value in its body off to a nonisolated async function as a sending parameter. rdar://134409359
1 parent a4a157c commit 3cfcc17

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,9 +1226,12 @@ struct PartitionOpEvaluator {
12261226
}
12271227

12281228
// Next see if we are disconnected and have the same isolation. In such a
1229-
// case, we do not transfer since the disconnected value is allowed to be
1230-
// resued after we return.
1231-
if (transferredRegionIsolation.isDisconnected() && calleeIsolationInfo &&
1229+
// case, if we are not marked explicitly as sending, we do not transfer
1230+
// since the disconnected value is allowed to be resued after we
1231+
// return. If we are passed as a sending parameter, we cannot do this.
1232+
if (auto fas = FullApplySite::isa(op.getSourceInst());
1233+
(!fas || !fas.isSending(*op.getSourceOp())) &&
1234+
transferredRegionIsolation.isDisconnected() && calleeIsolationInfo &&
12321235
transferredRegionIsolation.hasSameIsolation(calleeIsolationInfo))
12331236
return;
12341237

test/Concurrency/transfernonsendable_sending_params.swift

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
// MARK: Declarations //
88
////////////////////////
99

10-
class NonSendableKlass {}
10+
class NonSendableKlass {
11+
func use() {}
12+
}
1113

1214
struct NonSendableStruct {
1315
var first = NonSendableKlass()
@@ -55,6 +57,9 @@ func throwingFunction() throws { fatalError() }
5557
func transferArg(_ x: sending NonSendableKlass) {
5658
}
5759

60+
func transferArgAsync(_ x: sending NonSendableKlass) async {
61+
}
62+
5863
func transferArgWithOtherParam(_ x: sending NonSendableKlass, _ y: NonSendableKlass) {
5964
}
6065

@@ -559,3 +564,56 @@ extension MyActor {
559564
}
560565
}
561566
}
567+
568+
// We would normally not error here since transferArg is nonisolated and c is
569+
// disconnected. Since c is passed as sending, we shouldn't squelch this.
570+
func disconnectedPassedSendingToNonIsolatedCallee(
571+
) async -> Void {
572+
let c = NonSendableKlass()
573+
transferArg(c) // expected-warning {{sending 'c' risks causing data races}}
574+
// expected-note @-1 {{'c' used after being passed as a 'sending' parameter}}
575+
c.use() // expected-note {{access can happen concurrently}}
576+
}
577+
578+
// We would normally not error here since transferArg is nonisolated and c is
579+
// disconnected. Since c is passed as sending, we shouldn't squelch this.
580+
func disconnectedPassedSendingToAsyncNonIsolatedCallee(
581+
) async -> Void {
582+
let c = NonSendableKlass()
583+
await transferArgAsync(c) // expected-warning {{sending 'c' risks causing data races}}
584+
// expected-note @-1 {{'c' used after being passed as a 'sending' parameter}}
585+
c.use() // expected-note {{access can happen concurrently}}
586+
}
587+
588+
// We would normally not error here since transferArg is nonisolated and c is
589+
// disconnected. Since c is passed as sending, we shouldn't squelch this.
590+
func disconnectedPassedSendingToNonIsolatedCalleeIsolatedParam2(
591+
isolation: isolated (any Actor)? = nil
592+
) async -> Void {
593+
let c = NonSendableKlass()
594+
transferArg(c) // expected-warning {{sending 'c' risks causing data races}}
595+
// expected-note @-1 {{'c' used after being passed as a 'sending' parameter}}
596+
c.use() // expected-note {{access can happen concurrently}}
597+
}
598+
599+
// We would normally not error here since transferArg is nonisolated and c is
600+
// disconnected. Since c is passed as sending, we shouldn't squelch this.
601+
func disconnectedPassedSendingToAsyncNonIsolatedCalleeIsolatedParam2(
602+
isolation: isolated (any Actor)? = nil
603+
) async -> Void {
604+
let c = NonSendableKlass()
605+
await transferArgAsync(c) // expected-warning {{sending 'c' risks causing data races}}
606+
// expected-note @-1 {{'c' used after being passed as a 'sending' parameter}}
607+
c.use() // expected-note {{access can happen concurrently}}
608+
}
609+
610+
// We would normally not error here since transferArg is nonisolated and c is
611+
// disconnected. Since c is passed as sending, we shouldn't squelch this.
612+
func disconnectedPassedSendingToNonIsolatedCalleeIsolatedParam3(
613+
isolation: isolated (any Actor)? = nil
614+
) -> Void {
615+
let c = NonSendableKlass()
616+
transferArg(c) // expected-warning {{sending 'c' risks causing data races}}
617+
// expected-note @-1 {{'c' used after being passed as a 'sending' parameter}}
618+
c.use() // expected-note {{access can happen concurrently}}
619+
}

0 commit comments

Comments
 (0)