Skip to content

Commit 24bb04b

Browse files
committed
[region-isolation] Add support for global actors.
rdar://119099990
1 parent 556c503 commit 24bb04b

File tree

2 files changed

+200
-2
lines changed

2 files changed

+200
-2
lines changed

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/SIL/NodeDatastructures.h"
2323
#include "swift/SIL/OperandDatastructures.h"
2424
#include "swift/SIL/OwnershipUtils.h"
25+
#include "swift/SIL/PatternMatch.h"
2526
#include "swift/SIL/SILBasicBlock.h"
2627
#include "swift/SIL/SILBuilder.h"
2728
#include "swift/SIL/SILFunction.h"
@@ -36,6 +37,7 @@
3637

3738
using namespace swift;
3839
using namespace swift::PartitionPrimitives;
40+
using namespace swift::PatternMatch;
3941

4042
//===----------------------------------------------------------------------===//
4143
// MARK: Utilities
@@ -292,6 +294,26 @@ static bool isAsyncLetBeginPartialApply(PartialApplyInst *pai) {
292294
return *kind == BuiltinValueKind::StartAsyncLetWithLocalBuffer;
293295
}
294296

297+
static bool isGlobalActorInit(SILFunction *fn) {
298+
auto block = fn->begin();
299+
300+
// Make sure our function has a single block. We should always have a single
301+
// block today. Return nullptr otherwise.
302+
if (block == fn->end() || std::next(block) != fn->end())
303+
return false;
304+
305+
GlobalAddrInst *gai = nullptr;
306+
if (!match(cast<SILInstruction>(block->getTerminator()),
307+
m_ReturnInst(m_AddressToPointerInst(m_GlobalAddrInst(gai)))))
308+
return false;
309+
310+
auto *globalDecl = gai->getReferencedGlobal()->getDecl();
311+
if (!globalDecl)
312+
return false;
313+
314+
return globalDecl->getGlobalActorAttr() != std::nullopt;
315+
}
316+
295317
//===----------------------------------------------------------------------===//
296318
// MARK: Diagnostics
297319
//===----------------------------------------------------------------------===//
@@ -809,6 +831,17 @@ class PartitionOpTranslator {
809831
}
810832
}
811833

834+
// Check if we have an unsafeMutableAddressor from a global actor, mark the
835+
// returned value as being actor derived.
836+
if (auto applySite = FullApplySite::isa(iter.first->first)) {
837+
if (auto *calleeFunction = applySite.getCalleeFunction()) {
838+
if (calleeFunction->isGlobalInit() &&
839+
isGlobalActorInit(calleeFunction)) {
840+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
841+
}
842+
}
843+
}
844+
812845
// If our access storage is from a class, then see if we have an actor. In
813846
// such a case, we need to add this id to the neverTransferred set.
814847

@@ -1294,6 +1327,14 @@ class PartitionOpTranslator {
12941327
"srcID and dstID are different?!");
12951328
}
12961329

1330+
void translateSILLookThrough(SingleValueInstruction *svi) {
1331+
assert(svi->getNumOperands() == 1);
1332+
auto srcID = tryToTrackValue(svi->getOperand(0));
1333+
auto destID = tryToTrackValue(svi);
1334+
assert(((!destID || !srcID) || destID->getID() == srcID->getID()) &&
1335+
"srcID and dstID are different?!");
1336+
}
1337+
12971338
template <typename Collection>
12981339
void translateSILAssign(SILValue dest, Collection collection) {
12991340
return translateSILMultiAssign(TinyPtrVector<SILValue>(dest), collection);
@@ -1518,6 +1559,21 @@ class PartitionOpTranslator {
15181559
return translateSILLookThrough(inst->getResult(0), inst->getOperand(0));
15191560
return translateSILAssign(inst);
15201561

1562+
case SILInstructionKind::PointerToAddressInst: {
1563+
auto *atpi = cast<PointerToAddressInst>(inst);
1564+
1565+
// A raw pointer is considered to be a non-Sendable type. If we cast it to
1566+
// a Sendable type, treat it as a require. We can assume that if the user
1567+
// casted it to a Sendable type, if the type were not actually sendable,
1568+
// it would be undefined behavior.
1569+
if (!isNonSendableType(atpi->getType())) {
1570+
return translateSILRequire(atpi->getOperand());
1571+
}
1572+
1573+
// Otherwise, if we have a non-Sendable type, look through it.
1574+
return translateSILAssign(atpi);
1575+
}
1576+
15211577
// Just make the result part of the operand's region without requiring.
15221578
//
15231579
// This is appropriate for things like object casts and object
@@ -1534,8 +1590,6 @@ class PartitionOpTranslator {
15341590
case SILInstructionKind::InitExistentialRefInst:
15351591
case SILInstructionKind::OpenExistentialBoxInst:
15361592
case SILInstructionKind::OpenExistentialRefInst:
1537-
case SILInstructionKind::PointerToAddressInst:
1538-
case SILInstructionKind::ProjectBlockStorageInst:
15391593
case SILInstructionKind::RefToUnmanagedInst:
15401594
case SILInstructionKind::TailAddrInst:
15411595
case SILInstructionKind::ThickToObjCMetatypeInst:
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
// RUN: %target-swift-frontend -emit-sil -strict-concurrency=complete -disable-availability-checking -verify -verify-additional-prefix complete- %s -o /dev/null -parse-as-library
2+
// RUN: %target-swift-frontend -emit-sil -strict-concurrency=complete -enable-experimental-feature RegionBasedIsolation -disable-availability-checking -verify -verify-additional-prefix tns- %s -o /dev/null -parse-as-library
3+
4+
// REQUIRES: concurrency
5+
// REQUIRES: asserts
6+
7+
////////////////////////
8+
// MARK: Declarations //
9+
////////////////////////
10+
11+
class NonSendableKlass {}
12+
final class SendableKlass : Sendable {}
13+
14+
actor GlobalActorInstance {}
15+
16+
@globalActor
17+
struct GlobalActor {
18+
static let shared = GlobalActorInstance()
19+
}
20+
21+
func transferToNonIsolated<T>(_ t: T) async {}
22+
@MainActor func transferToMainActor<T>(_ t: T) async {}
23+
@GlobalActor func transferToGlobalActor<T>(_ t: T) async {}
24+
func useValue<T>(_ t: T) {}
25+
26+
var booleanFlag: Bool { false }
27+
28+
/////////////////
29+
// MARK: Tests //
30+
/////////////////
31+
32+
private class NonSendableLinkedList<T> { // expected-complete-note 5{{}}
33+
var listHead: NonSendableLinkedListNode<T>?
34+
35+
init() { listHead = nil }
36+
}
37+
38+
private class NonSendableLinkedListNode<T> { // expected-complete-note 3{{}}
39+
var next: NonSendableLinkedListNode?
40+
var data: T?
41+
42+
init() { next = nil }
43+
}
44+
45+
@GlobalActor private var firstList = NonSendableLinkedList<Int>()
46+
@GlobalActor private var secondList = NonSendableLinkedList<Int>()
47+
48+
@GlobalActor func useGlobalActor1() async {
49+
let x = firstList
50+
51+
await transferToMainActor(x) // expected-tns-warning {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
52+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableLinkedList<Int>' into main actor-isolated context may introduce data races}}
53+
54+
let y = secondList.listHead!.next!
55+
56+
await transferToMainActor(y) // expected-tns-warning {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
57+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableLinkedListNode<Int>' into main actor-isolated context may introduce data races}}
58+
}
59+
60+
@GlobalActor func useGlobalActor2() async {
61+
var x = NonSendableLinkedListNode<Int>()
62+
63+
if booleanFlag {
64+
x = secondList.listHead!.next!
65+
}
66+
67+
await transferToMainActor(x) // expected-tns-warning {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
68+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableLinkedListNode<Int>' into main actor-isolated context may introduce data races}}
69+
}
70+
71+
@GlobalActor func useGlobalActor3() async {
72+
var x = NonSendableLinkedListNode<Int>()
73+
74+
if booleanFlag {
75+
x = secondList.listHead!.next!
76+
}
77+
78+
await transferToGlobalActor(x)
79+
}
80+
81+
@GlobalActor func useGlobalActor4() async {
82+
let x = NonSendableLinkedListNode<Int>()
83+
84+
await transferToGlobalActor(x)
85+
86+
useValue(x)
87+
}
88+
89+
@GlobalActor func useGlobalActor5() async {
90+
let x = NonSendableLinkedListNode<Int>()
91+
92+
await transferToNonIsolated(x) // expected-tns-warning {{passing argument of non-sendable type 'NonSendableLinkedListNode<Int>' from global actor 'GlobalActor'-isolated context to nonisolated context at this call site could yield a race with accesses later in this function}}
93+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableLinkedListNode<Int>' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
94+
95+
useValue(x) // expected-tns-note {{access here could race}}
96+
}
97+
98+
private struct StructContainingValue { // expected-complete-note 2{{}}
99+
var x = NonSendableLinkedList<Int>()
100+
var y = SendableKlass()
101+
}
102+
103+
@GlobalActor func useGlobalActor6() async {
104+
var x = StructContainingValue()
105+
x = StructContainingValue()
106+
107+
await transferToNonIsolated(x) // expected-tns-warning {{passing argument of non-sendable type 'StructContainingValue' from global actor 'GlobalActor'-isolated context to nonisolated context at this call site could yield a race with accesses later in this function}}
108+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'StructContainingValue' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
109+
110+
useValue(x) // expected-tns-note {{access here could race}}
111+
}
112+
113+
@GlobalActor func useGlobalActor7() async {
114+
var x = StructContainingValue()
115+
x.x = firstList
116+
117+
await transferToNonIsolated(x) // expected-tns-warning {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
118+
// expected-complete-warning @-1 {{passing argument of non-sendable type 'StructContainingValue' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
119+
120+
useValue(x)
121+
}
122+
123+
@GlobalActor func useGlobalActor8() async {
124+
var x = (NonSendableLinkedList<Int>(), NonSendableLinkedList<Int>())
125+
x = (NonSendableLinkedList<Int>(), NonSendableLinkedList<Int>())
126+
127+
await transferToNonIsolated(x) // expected-tns-warning {{passing argument of non-sendable type '(NonSendableLinkedList<Int>, NonSendableLinkedList<Int>)' from global actor 'GlobalActor'-isolated context to nonisolated context at this call site could yield a race with accesses later in this function}}
128+
// expected-complete-warning @-1 {{passing argument of non-sendable type '(NonSendableLinkedList<Int>, NonSendableLinkedList<Int>)' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
129+
// expected-complete-warning @-2 {{passing argument of non-sendable type '(NonSendableLinkedList<Int>, NonSendableLinkedList<Int>)' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
130+
131+
useValue(x) // expected-tns-note {{access here could race}}
132+
}
133+
134+
@GlobalActor func useGlobalActor9() async {
135+
var x = (NonSendableLinkedList<Int>(), NonSendableLinkedList<Int>())
136+
137+
x.1 = firstList
138+
139+
await transferToNonIsolated(x) // expected-tns-warning {{call site passes `self` or a non-sendable argument of this function to another thread, potentially yielding a race with the caller}}
140+
// expected-complete-warning @-1 {{passing argument of non-sendable type '(NonSendableLinkedList<Int>, NonSendableLinkedList<Int>)' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
141+
// expected-complete-warning @-2 {{passing argument of non-sendable type '(NonSendableLinkedList<Int>, NonSendableLinkedList<Int>)' outside of global actor 'GlobalActor'-isolated context may introduce data races}}
142+
143+
useValue(x)
144+
}

0 commit comments

Comments
 (0)