Skip to content

Commit a6901d8

Browse files
authored
Merge pull request #81571 from gottesmm/pr-d7a7ebea7af14e2f9f1ce266febd0c983e3fc888
[rbi] Treat a partial_apply as nonisolated(unsafe) if all of its captures are nonisolated(unsafe).
2 parents 7e63d08 + 010443c commit a6901d8

File tree

3 files changed

+294
-6
lines changed

3 files changed

+294
-6
lines changed

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2503,7 +2503,8 @@ class PartitionOpTranslator {
25032503
}
25042504
}
25052505

2506-
if (auto isolationRegionInfo = SILIsolationInfo::get(pai)) {
2506+
if (auto isolationRegionInfo = SILIsolationInfo::get(pai);
2507+
isolationRegionInfo && !isolationRegionInfo.isDisconnected()) {
25072508
return translateIsolatedPartialApply(pai, isolationRegionInfo);
25082509
}
25092510

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

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

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

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

442515
if (actorIsolation.isGlobalActor()) {
443516
return SILIsolationInfo::getGlobalActorIsolated(
444-
pai, actorIsolation.getGlobalActor());
517+
pai, actorIsolation.getGlobalActor())
518+
.withUnsafeNonIsolated(partialApplyIsNonIsolatedUnsafe);
445519
}
446520

447521
if (actorIsolation.isActorInstanceIsolated()) {
@@ -465,13 +539,16 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
465539
if (auto *fArg = dyn_cast<SILFunctionArgument>(
466540
actualIsolatedValue.getValue())) {
467541
if (auto info =
468-
SILIsolationInfo::getActorInstanceIsolated(pai, fArg))
542+
SILIsolationInfo::getActorInstanceIsolated(pai, fArg)
543+
.withUnsafeNonIsolated(
544+
partialApplyIsNonIsolatedUnsafe))
469545
return info;
470546
}
471547
}
472548

473549
return SILIsolationInfo::getActorInstanceIsolated(
474-
pai, actorInstance, actorIsolation.getActor());
550+
pai, actorInstance, actorIsolation.getActor())
551+
.withUnsafeNonIsolated(partialApplyIsNonIsolatedUnsafe);
475552
}
476553

477554
// For now, if we do not have an actor instance, just create an actor
@@ -485,12 +562,16 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
485562
//
486563
// TODO: How do we want to resolve this.
487564
return SILIsolationInfo::getPartialApplyActorInstanceIsolated(
488-
pai, actorIsolation.getActor());
565+
pai, actorIsolation.getActor())
566+
.withUnsafeNonIsolated(partialApplyIsNonIsolatedUnsafe);
489567
}
490568

491569
assert(actorIsolation.getKind() != ActorIsolation::Erased &&
492570
"Implement this!");
493571
}
572+
573+
if (partialApplyIsNonIsolatedUnsafe)
574+
return SILIsolationInfo::getDisconnected(partialApplyIsNonIsolatedUnsafe);
494575
}
495576

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

test/Concurrency/transfernonsendable_nonisolatedunsafe.swift

Lines changed: 207 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,209 @@ 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(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(priority:operation:)' risks causing races in between local and caller code}}
1027+
Task.detached { _ = x4a; _ = x4b } // expected-note {{access can happen concurrently}}
1028+
}
1029+
}

0 commit comments

Comments
 (0)