Skip to content

Commit 5361765

Browse files
authored
Merge pull request #82428 from gottesmm/release/6.2-144111950
[6.2][rbi] Treat a partial_apply as nonisolated(unsafe) if all of its captures are nonisolated(unsafe).
2 parents 1cc5a86 + b81f6bb commit 5361765

File tree

3 files changed

+308
-6
lines changed

3 files changed

+308
-6
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2482,7 +2482,8 @@ class PartitionOpTranslator {
24822482
}
24832483
}
24842484

2485-
if (auto isolationRegionInfo = SILIsolationInfo::get(pai)) {
2485+
if (auto isolationRegionInfo = SILIsolationInfo::get(pai);
2486+
isolationRegionInfo && !isolationRegionInfo.isDisconnected()) {
24862487
return translateIsolatedPartialApply(pai, isolationRegionInfo);
24872488
}
24882489

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 85 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,69 @@ inferIsolationInfoForTempAllocStack(AllocStackInst *asi) {
363363
return SILIsolationInfo::get(targetOperand->getUser());
364364
}
365365

366+
static SILValue lookThroughNonVarDeclOwnershipInsts(SILValue v) {
367+
while (true) {
368+
if (auto *svi = dyn_cast<SingleValueInstruction>(v)) {
369+
if (isa<CopyValueInst>(svi)) {
370+
v = svi->getOperand(0);
371+
continue;
372+
}
373+
374+
if (auto *bbi = dyn_cast<BeginBorrowInst>(v)) {
375+
if (!bbi->isFromVarDecl()) {
376+
v = bbi->getOperand();
377+
continue;
378+
}
379+
return v;
380+
}
381+
382+
if (auto *mvi = dyn_cast<MoveValueInst>(v)) {
383+
if (!mvi->isFromVarDecl()) {
384+
v = mvi->getOperand();
385+
continue;
386+
}
387+
388+
return v;
389+
}
390+
}
391+
392+
return v;
393+
}
394+
}
395+
396+
/// See if \p pai has at least one nonisolated(unsafe) capture and that all
397+
/// captures are either nonisolated(unsafe) or sendable.
398+
static bool isPartialApplyNonisolatedUnsafe(PartialApplyInst *pai) {
399+
bool foundOneNonIsolatedUnsafe = false;
400+
for (auto &op : pai->getArgumentOperands()) {
401+
if (SILIsolationInfo::isSendableType(op.get()))
402+
continue;
403+
404+
// Normally we would not look through copy_value, begin_borrow, or
405+
// move_value since this is meant to find the inherent isolation of a
406+
// specific element. But since we are checking for captures rather
407+
// than the actual value itself (just for unsafe nonisolated
408+
// purposes), it is ok.
409+
//
410+
// E.x.: As an example of something we want to prevent, consider an
411+
// invocation of a nonisolated function that is a parameter to an
412+
// @MainActor function. That means from a region isolation
413+
// perspective, the function parameter is in the MainActor region, but
414+
// the actual function itself is not MainActor isolated, since the
415+
// function will not hop onto the main actor.
416+
//
417+
// TODO: We should use some of the shared infrastructure to find the
418+
// underlying value of op.get(). This is conservatively correct for
419+
// now.
420+
auto value = lookThroughNonVarDeclOwnershipInsts(op.get());
421+
if (!SILIsolationInfo::get(value).isUnsafeNonIsolated())
422+
return false;
423+
foundOneNonIsolatedUnsafe = true;
424+
}
425+
426+
return foundOneNonIsolatedUnsafe;
427+
}
428+
366429
SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
367430
if (auto fas = FullApplySite::isa(inst)) {
368431
// Before we do anything, see if we have a sending result. In such a case,
@@ -442,12 +505,23 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
442505
}
443506

444507
if (auto *pai = dyn_cast<PartialApplyInst>(inst)) {
508+
// Check if we have any captures and if the isolation info for all captures
509+
// are nonisolated(unsafe) or Sendable. In such a case, we consider the
510+
// partial_apply to be nonisolated(unsafe). We purposely do not do this if
511+
// the partial_apply does not have any parameters just out of paranoia... we
512+
// shouldn't have such a partial_apply emitted by SILGen (it should use a
513+
// thin to thick function or something like that)... but in that case since
514+
// we do not have any nonisolated(unsafe), it doesn't make sense to
515+
// propagate nonisolated(unsafe).
516+
bool partialApplyIsNonIsolatedUnsafe = isPartialApplyNonisolatedUnsafe(pai);
517+
445518
if (auto *ace = pai->getLoc().getAsASTNode<AbstractClosureExpr>()) {
446519
auto actorIsolation = ace->getActorIsolation();
447520

448521
if (actorIsolation.isGlobalActor()) {
449522
return SILIsolationInfo::getGlobalActorIsolated(
450-
pai, actorIsolation.getGlobalActor());
523+
pai, actorIsolation.getGlobalActor())
524+
.withUnsafeNonIsolated(partialApplyIsNonIsolatedUnsafe);
451525
}
452526

453527
if (actorIsolation.isActorInstanceIsolated()) {
@@ -471,13 +545,16 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
471545
if (auto *fArg = dyn_cast<SILFunctionArgument>(
472546
actualIsolatedValue.getValue())) {
473547
if (auto info =
474-
SILIsolationInfo::getActorInstanceIsolated(pai, fArg))
548+
SILIsolationInfo::getActorInstanceIsolated(pai, fArg)
549+
.withUnsafeNonIsolated(
550+
partialApplyIsNonIsolatedUnsafe))
475551
return info;
476552
}
477553
}
478554

479555
return SILIsolationInfo::getActorInstanceIsolated(
480-
pai, actorInstance, actorIsolation.getActor());
556+
pai, actorInstance, actorIsolation.getActor())
557+
.withUnsafeNonIsolated(partialApplyIsNonIsolatedUnsafe);
481558
}
482559

483560
// For now, if we do not have an actor instance, just create an actor
@@ -491,12 +568,16 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
491568
//
492569
// TODO: How do we want to resolve this.
493570
return SILIsolationInfo::getPartialApplyActorInstanceIsolated(
494-
pai, actorIsolation.getActor());
571+
pai, actorIsolation.getActor())
572+
.withUnsafeNonIsolated(partialApplyIsNonIsolatedUnsafe);
495573
}
496574

497575
assert(actorIsolation.getKind() != ActorIsolation::Erased &&
498576
"Implement this!");
499577
}
578+
579+
if (partialApplyIsNonIsolatedUnsafe)
580+
return SILIsolationInfo::getDisconnected(partialApplyIsNonIsolatedUnsafe);
500581
}
501582

502583
// See if the memory base is a ref_element_addr from an address. If so, add

test/Concurrency/transfernonsendable_nonisolatedunsafe.swift

Lines changed: 221 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
// MARK: Declarations //
1111
////////////////////////
1212

13-
class NonSendableKlass { // expected-complete-note 98{{}}
13+
class NonSendableKlass {
1414
var field: NonSendableKlass? = nil
1515
}
1616

@@ -821,3 +821,223 @@ actor ActorContainingSendableStruct {
821821
}
822822

823823

824+
////////////////////
825+
// MARK: Closures //
826+
////////////////////
827+
828+
func closureTests() async {
829+
func sendingClosure(_ x: sending () -> ()) {
830+
}
831+
832+
func testLetOneNSVariableError() async {
833+
let x = NonSendableKlass()
834+
sendingClosure { _ = x } // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
835+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
836+
sendingClosure { _ = x } // expected-note {{access can happen concurrently}}
837+
}
838+
839+
func testLetNonIsolatedUnsafeNSVariableNoError() async {
840+
nonisolated(unsafe) let x = NonSendableKlass()
841+
sendingClosure { _ = x }
842+
sendingClosure { _ = x }
843+
}
844+
845+
func testLetOneNSVariableSVariableError() async {
846+
let x = NonSendableKlass()
847+
let y = CustomActorInstance()
848+
sendingClosure { // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
849+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
850+
_ = x
851+
_ = y
852+
}
853+
sendingClosure { // expected-note {{access can happen concurrently}}
854+
_ = x
855+
_ = y
856+
}
857+
}
858+
859+
func testLetNonIsolatedUnsafeNSSVariableNoError() async {
860+
nonisolated(unsafe) let x = NonSendableKlass()
861+
let y = CustomActorInstance()
862+
sendingClosure {
863+
_ = x
864+
_ = y
865+
}
866+
sendingClosure {
867+
_ = x
868+
_ = y
869+
}
870+
}
871+
872+
func testLetTwoNSVariableError() async {
873+
let x = NonSendableKlass()
874+
let y = NonSendableKlass()
875+
sendingClosure { // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
876+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
877+
_ = x
878+
_ = y
879+
}
880+
sendingClosure { // expected-note {{access can happen concurrently}}
881+
_ = x
882+
_ = y
883+
}
884+
}
885+
886+
func testLetTwoNSVariableError2() async {
887+
nonisolated(unsafe) let x = NonSendableKlass()
888+
let y = NonSendableKlass()
889+
sendingClosure { // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
890+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
891+
_ = x
892+
_ = y
893+
}
894+
sendingClosure { // expected-note {{access can happen concurrently}}
895+
_ = x
896+
_ = y
897+
}
898+
}
899+
900+
func testLetTwoNSVariableError3() async {
901+
nonisolated(unsafe) let x = NonSendableKlass()
902+
nonisolated(unsafe) let y = NonSendableKlass()
903+
sendingClosure {
904+
_ = x
905+
_ = y
906+
}
907+
sendingClosure {
908+
_ = x
909+
_ = y
910+
}
911+
}
912+
913+
func testVarOneNSVariableError() async {
914+
var x = NonSendableKlass()
915+
x = NonSendableKlass()
916+
917+
sendingClosure { _ = x } // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
918+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
919+
sendingClosure { _ = x } // expected-note {{access can happen concurrently}}
920+
}
921+
922+
func testVarNonIsolatedUnsafeNSVariableNoError() async {
923+
nonisolated(unsafe) var x = NonSendableKlass()
924+
x = NonSendableKlass()
925+
926+
sendingClosure { _ = x }
927+
sendingClosure { _ = x }
928+
}
929+
930+
func testVarOneNSVariableSVariableError() async {
931+
var x = NonSendableKlass()
932+
x = NonSendableKlass()
933+
var y = CustomActorInstance()
934+
y = CustomActorInstance()
935+
sendingClosure { // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
936+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
937+
_ = x
938+
_ = y
939+
}
940+
sendingClosure { // expected-note {{access can happen concurrently}}
941+
_ = x
942+
_ = y
943+
}
944+
}
945+
946+
func testVarNonIsolatedUnsafeNSSVariableNoError() async {
947+
nonisolated(unsafe) var x = NonSendableKlass()
948+
x = NonSendableKlass()
949+
var y = CustomActorInstance()
950+
y = CustomActorInstance()
951+
sendingClosure {
952+
_ = x
953+
_ = y
954+
}
955+
sendingClosure {
956+
_ = x
957+
_ = y
958+
}
959+
}
960+
961+
func testVarTwoNSVariableError() async {
962+
var x = NonSendableKlass()
963+
x = NonSendableKlass()
964+
var y = NonSendableKlass()
965+
y = NonSendableKlass()
966+
sendingClosure { // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
967+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
968+
_ = x
969+
_ = y
970+
}
971+
sendingClosure { // expected-note {{access can happen concurrently}}
972+
_ = x
973+
_ = y
974+
}
975+
}
976+
977+
func testVarTwoNSVariableError2() async {
978+
nonisolated(unsafe) var x = NonSendableKlass()
979+
x = NonSendableKlass()
980+
var y = NonSendableKlass()
981+
y = NonSendableKlass()
982+
sendingClosure { // expected-warning {{sending value of non-Sendable type '() -> ()' risks causing data races}}
983+
// expected-note @-1 {{Passing value of non-Sendable type '() -> ()' as a 'sending' argument to local function 'sendingClosure' risks causing races in between local and caller code}}
984+
_ = x
985+
_ = y
986+
}
987+
sendingClosure { // expected-note {{access can happen concurrently}}
988+
_ = x
989+
_ = y
990+
}
991+
}
992+
993+
func testVarTwoNSVariableError3() async {
994+
nonisolated(unsafe) var x = NonSendableKlass()
995+
x = NonSendableKlass()
996+
nonisolated(unsafe) var y = NonSendableKlass()
997+
y = NonSendableKlass()
998+
sendingClosure {
999+
_ = x
1000+
_ = y
1001+
}
1002+
sendingClosure {
1003+
_ = x
1004+
_ = y
1005+
}
1006+
}
1007+
1008+
func testWithTaskDetached() async {
1009+
let x1 = NonSendableKlass()
1010+
Task.detached { _ = x1 } // expected-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}}
1011+
// expected-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to static method 'detached(name:priority:operation:)' risks causing races in between local and caller code}}
1012+
Task.detached { _ = x1 } // expected-note {{access can happen concurrently}}
1013+
1014+
nonisolated(unsafe) let x2 = NonSendableKlass()
1015+
Task.detached { _ = x2 }
1016+
Task.detached { _ = x2 }
1017+
1018+
nonisolated(unsafe) let x3a = NonSendableKlass()
1019+
nonisolated(unsafe) let x3b = NonSendableKlass()
1020+
Task.detached { _ = x3a; _ = x3b }
1021+
Task.detached { _ = x3a; _ = x3b }
1022+
1023+
nonisolated(unsafe) let x4a = NonSendableKlass()
1024+
let x4b = NonSendableKlass()
1025+
Task.detached { _ = x4a; _ = x4b } // expected-warning {{sending value of non-Sendable type '() async -> ()' risks causing data races}}
1026+
// expected-note @-1 {{Passing value of non-Sendable type '() async -> ()' as a 'sending' argument to static method 'detached(name:priority:operation:)' risks causing races in between local and caller code}}
1027+
Task.detached { _ = x4a; _ = x4b } // expected-note {{access can happen concurrently}}
1028+
}
1029+
1030+
// The reason why this works is that we do not infer nonisolated(unsafe)
1031+
// passed the begin_borrow [var_decl] of y. So we think the closure is
1032+
// nonisolated(unsafe), but its uses via the begin_borrow [var_decl] is
1033+
// not.
1034+
func testNamedClosure() async {
1035+
nonisolated(unsafe) let x = NonSendableKlass()
1036+
let y = {
1037+
_ = x
1038+
}
1039+
sendingClosure(y) // expected-warning {{sending 'y' risks causing data races}}
1040+
// expected-note @-1 {{'y' used after being passed as a 'sending' parameter}}
1041+
sendingClosure(y) // expected-note {{access can happen concurrently}}
1042+
}
1043+
}

0 commit comments

Comments
 (0)