Skip to content

Commit cff1c77

Browse files
committed
[rbi] Teach RBI how to infer nonisolated(unsafe) of closure captures.
The previous commit in this PR exposed that we were not handling this correctly. Specifically, we incorrectly started to error in SwiftFoundation. rdar://162629359
1 parent 97c3bf3 commit cff1c77

File tree

2 files changed

+97
-9
lines changed

2 files changed

+97
-9
lines changed

lib/SILOptimizer/Utils/SILIsolationInfo.cpp

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -990,6 +990,21 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
990990
if (fArg->isSending())
991991
return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/);
992992

993+
// Check if we have a closure captured parameter that is nonisolated(unsafe)
994+
// at its original declaration sign. In such a case, we want to propagate that
995+
// bit.
996+
bool isClosureCapturedNonisolatedUnsafe = [&]() -> bool {
997+
if (!fArg->isClosureCapture())
998+
return false;
999+
auto *decl = fArg->getDecl();
1000+
if (!decl)
1001+
return false;
1002+
auto *attr = decl->getAttrs().getAttribute<NonisolatedAttr>();
1003+
if (!attr)
1004+
return false;
1005+
return attr->isUnsafe();
1006+
}();
1007+
9931008
// If we have a closure capture that is not an indirect result or indirect
9941009
// result error, we want to treat it as sending so that we properly handle
9951010
// async lets.
@@ -1001,7 +1016,8 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10011016
fArg->isClosureCapture()) {
10021017
if (auto declRef = arg->getFunction()->getDeclRef();
10031018
declRef && declRef.isAsyncLetClosure) {
1004-
return SILIsolationInfo::getDisconnected(false /*nonisolated(unsafe)*/);
1019+
return SILIsolationInfo::getDisconnected(
1020+
isClosureCapturedNonisolatedUnsafe);
10051021
}
10061022
}
10071023

@@ -1014,12 +1030,20 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10141030
if (auto funcIsolation = fArg->getFunction()->getActorIsolation();
10151031
funcIsolation && funcIsolation->isCallerIsolationInheriting()) {
10161032
return SILIsolationInfo::getTaskIsolated(fArg)
1017-
.withNonisolatedNonsendingTaskIsolated(true);
1033+
.withNonisolatedNonsendingTaskIsolated(true)
1034+
.withUnsafeNonIsolated(isClosureCapturedNonisolatedUnsafe);
10181035
}
10191036

10201037
auto astType = isolatedArg->getType().getASTType();
10211038
if (astType->lookThroughAllOptionalTypes()->getAnyActor()) {
1022-
return SILIsolationInfo::getActorInstanceIsolated(fArg, isolatedArg);
1039+
if (auto isolation =
1040+
SILIsolationInfo::getActorInstanceIsolated(fArg, isolatedArg)) {
1041+
return isolation.withUnsafeNonIsolated(
1042+
isClosureCapturedNonisolatedUnsafe);
1043+
}
1044+
// If we had an isolated parameter that was an actor type, but we did not
1045+
// find any isolation for the actor instance... return {}.
1046+
return {};
10231047
}
10241048
}
10251049

@@ -1061,12 +1085,14 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10611085
}
10621086

10631087
// Otherwise, if we do not have an isolated argument and are not in an
1064-
// allocator, then we might be isolated via global isolation.
1088+
// allocator, then we might be isolated via global isolation or in a global
1089+
// actor isolated initializer.
10651090
if (auto functionIsolation = fArg->getFunction()->getActorIsolation()) {
10661091
if (functionIsolation->isActorIsolated()) {
10671092
if (functionIsolation->isGlobalActor()) {
10681093
return SILIsolationInfo::getGlobalActorIsolated(
1069-
fArg, functionIsolation->getGlobalActor());
1094+
fArg, functionIsolation->getGlobalActor())
1095+
.withUnsafeNonIsolated(isClosureCapturedNonisolatedUnsafe);
10701096
}
10711097

10721098
return SILIsolationInfo::getActorInstanceIsolated(
@@ -1075,7 +1101,8 @@ SILIsolationInfo SILIsolationInfo::get(SILArgument *arg) {
10751101
}
10761102
}
10771103

1078-
return SILIsolationInfo::getTaskIsolated(fArg);
1104+
return SILIsolationInfo::getTaskIsolated(fArg).withUnsafeNonIsolated(
1105+
isClosureCapturedNonisolatedUnsafe);
10791106
}
10801107

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

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+
}

0 commit comments

Comments
 (0)