Skip to content

Commit 520e124

Browse files
authored
Merge pull request #70192 from gottesmm/pr-9508a33268dfb29c47747efbe4184b43b0352dd9
[region-isolation] Add support for global actors.
2 parents 4c2476d + 24bb04b commit 520e124

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
@@ -24,6 +24,7 @@
2424
#include "swift/SIL/NodeDatastructures.h"
2525
#include "swift/SIL/OperandDatastructures.h"
2626
#include "swift/SIL/OwnershipUtils.h"
27+
#include "swift/SIL/PatternMatch.h"
2728
#include "swift/SIL/SILBasicBlock.h"
2829
#include "swift/SIL/SILBuilder.h"
2930
#include "swift/SIL/SILFunction.h"
@@ -38,6 +39,7 @@
3839

3940
using namespace swift;
4041
using namespace swift::PartitionPrimitives;
42+
using namespace swift::PatternMatch;
4143

4244
namespace {
4345
using TransferringOperandSetFactory = Partition::TransferringOperandSetFactory;
@@ -298,6 +300,26 @@ static bool isAsyncLetBeginPartialApply(PartialApplyInst *pai) {
298300
return *kind == BuiltinValueKind::StartAsyncLetWithLocalBuffer;
299301
}
300302

303+
static bool isGlobalActorInit(SILFunction *fn) {
304+
auto block = fn->begin();
305+
306+
// Make sure our function has a single block. We should always have a single
307+
// block today. Return nullptr otherwise.
308+
if (block == fn->end() || std::next(block) != fn->end())
309+
return false;
310+
311+
GlobalAddrInst *gai = nullptr;
312+
if (!match(cast<SILInstruction>(block->getTerminator()),
313+
m_ReturnInst(m_AddressToPointerInst(m_GlobalAddrInst(gai)))))
314+
return false;
315+
316+
auto *globalDecl = gai->getReferencedGlobal()->getDecl();
317+
if (!globalDecl)
318+
return false;
319+
320+
return globalDecl->getGlobalActorAttr() != std::nullopt;
321+
}
322+
301323
//===----------------------------------------------------------------------===//
302324
// MARK: Diagnostics
303325
//===----------------------------------------------------------------------===//
@@ -1047,6 +1069,17 @@ class PartitionOpTranslator {
10471069
}
10481070
}
10491071

1072+
// Check if we have an unsafeMutableAddressor from a global actor, mark the
1073+
// returned value as being actor derived.
1074+
if (auto applySite = FullApplySite::isa(iter.first->first)) {
1075+
if (auto *calleeFunction = applySite.getCalleeFunction()) {
1076+
if (calleeFunction->isGlobalInit() &&
1077+
isGlobalActorInit(calleeFunction)) {
1078+
iter.first->getSecond().addFlag(TrackableValueFlag::isActorDerived);
1079+
}
1080+
}
1081+
}
1082+
10501083
// If our access storage is from a class, then see if we have an actor. In
10511084
// such a case, we need to add this id to the neverTransferred set.
10521085

@@ -1550,6 +1583,14 @@ class PartitionOpTranslator {
15501583
"srcID and dstID are different?!");
15511584
}
15521585

1586+
void translateSILLookThrough(SingleValueInstruction *svi) {
1587+
assert(svi->getNumOperands() == 1);
1588+
auto srcID = tryToTrackValue(svi->getOperand(0));
1589+
auto destID = tryToTrackValue(svi);
1590+
assert(((!destID || !srcID) || destID->getID() == srcID->getID()) &&
1591+
"srcID and dstID are different?!");
1592+
}
1593+
15531594
template <typename Collection>
15541595
void translateSILAssign(SILValue dest, Collection collection) {
15551596
return translateSILMultiAssign(TinyPtrVector<SILValue>(dest), collection);
@@ -1783,6 +1824,21 @@ class PartitionOpTranslator {
17831824
return translateSILLookThrough(inst->getResult(0), inst->getOperand(0));
17841825
return translateSILAssign(inst);
17851826

1827+
case SILInstructionKind::PointerToAddressInst: {
1828+
auto *atpi = cast<PointerToAddressInst>(inst);
1829+
1830+
// A raw pointer is considered to be a non-Sendable type. If we cast it to
1831+
// a Sendable type, treat it as a require. We can assume that if the user
1832+
// casted it to a Sendable type, if the type were not actually sendable,
1833+
// it would be undefined behavior.
1834+
if (!isNonSendableType(atpi->getType())) {
1835+
return translateSILRequire(atpi->getOperand());
1836+
}
1837+
1838+
// Otherwise, if we have a non-Sendable type, look through it.
1839+
return translateSILAssign(atpi);
1840+
}
1841+
17861842
// Just make the result part of the operand's region without requiring.
17871843
//
17881844
// This is appropriate for things like object casts and object
@@ -1799,8 +1855,6 @@ class PartitionOpTranslator {
17991855
case SILInstructionKind::InitExistentialRefInst:
18001856
case SILInstructionKind::OpenExistentialBoxInst:
18011857
case SILInstructionKind::OpenExistentialRefInst:
1802-
case SILInstructionKind::PointerToAddressInst:
1803-
case SILInstructionKind::ProjectBlockStorageInst:
18041858
case SILInstructionKind::RefToUnmanagedInst:
18051859
case SILInstructionKind::TailAddrInst:
18061860
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)