Skip to content

Commit 990243d

Browse files
authored
Merge pull request #74495 from gottesmm/release/6.0-129237675
[6.0][region-isolation] Make store_borrow a store operation that does not require
2 parents 41d4e2e + 2026f39 commit 990243d

File tree

7 files changed

+112
-80
lines changed

7 files changed

+112
-80
lines changed

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,14 +1057,6 @@ struct PartitionOpEvaluator {
10571057
"Assign PartitionOp should be passed 2 arguments");
10581058
assert(p.isTrackingElement(op.getOpArgs()[1]) &&
10591059
"Assign PartitionOp's source argument should be already tracked");
1060-
// If we are using a region that was transferred as our assignment source
1061-
// value... emit an error.
1062-
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[1])) {
1063-
for (auto transferredOperand : transferredOperandSet->data()) {
1064-
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[1],
1065-
transferredOperand);
1066-
}
1067-
}
10681060
p.assignElement(op.getOpArgs()[0], op.getOpArgs()[1]);
10691061
return;
10701062
case PartitionOpKind::AssignFresh:
@@ -1154,20 +1146,6 @@ struct PartitionOpEvaluator {
11541146
p.isTrackingElement(op.getOpArgs()[1]) &&
11551147
"Merge PartitionOp's arguments should already be tracked");
11561148

1157-
// if attempting to merge a transferred region, handle the failure
1158-
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[0])) {
1159-
for (auto transferredOperand : transferredOperandSet->data()) {
1160-
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[0],
1161-
transferredOperand);
1162-
}
1163-
}
1164-
if (auto *transferredOperandSet = p.getTransferred(op.getOpArgs()[1])) {
1165-
for (auto transferredOperand : transferredOperandSet->data()) {
1166-
handleLocalUseAfterTransferHelper(op, op.getOpArgs()[1],
1167-
transferredOperand);
1168-
}
1169-
}
1170-
11711149
p.merge(op.getOpArgs()[0], op.getOpArgs()[1]);
11721150
return;
11731151
case PartitionOpKind::Require:
@@ -1270,13 +1248,6 @@ struct PartitionOpEvaluator {
12701248
void handleLocalUseAfterTransferHelper(const PartitionOp &op, Element elt,
12711249
Operand *transferringOp) const {
12721250
if (shouldTryToSquelchErrors()) {
1273-
if (auto isolationInfo = getIsolationInfo(op)) {
1274-
if (isolationInfo.isActorIsolated() &&
1275-
isolationInfo.hasSameIsolation(
1276-
SILIsolationInfo::get(transferringOp->getUser())))
1277-
return;
1278-
}
1279-
12801251
if (SILValue equivalenceClassRep =
12811252
getRepresentative(transferringOp->get())) {
12821253

lib/SILOptimizer/Analysis/RegionAnalysis.cpp

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,8 @@ class PartitionOpTranslator {
16061606
void
16071607
translateSILMultiAssign(const TargetRange &resultValues,
16081608
const SourceRange &sourceValues,
1609-
SILIsolationInfo resultIsolationInfoOverride = {}) {
1609+
SILIsolationInfo resultIsolationInfoOverride = {},
1610+
bool requireSrcValues = true) {
16101611
SmallVector<SILValue, 8> assignOperands;
16111612
SmallVector<SILValue, 8> assignResults;
16121613

@@ -1672,9 +1673,17 @@ class PartitionOpTranslator {
16721673
}
16731674
}
16741675

1675-
// Require all srcs.
1676-
for (auto src : assignOperands)
1677-
builder.addRequire(src);
1676+
// Require all srcs if we are supposed to. (By default we do).
1677+
//
1678+
// DISCUSSION: The reason that this may be useful is for special
1679+
// instructions like store_borrow. On the one hand, we want store_borrow to
1680+
// act like a store in the sense that we want to combine the regions of its
1681+
// src and dest... but at the same time, we do not want to treat the store
1682+
// itself as a use of its parent value. We want that to be any subsequent
1683+
// uses of the store_borrow.
1684+
if (requireSrcValues)
1685+
for (auto src : assignOperands)
1686+
builder.addRequire(src);
16781687

16791688
// Merge all srcs.
16801689
for (unsigned i = 1; i < assignOperands.size(); i++) {
@@ -2136,21 +2145,28 @@ class PartitionOpTranslator {
21362145
}
21372146

21382147
template <typename Collection>
2139-
void translateSILMerge(SILValue dest, Collection collection) {
2148+
void translateSILMerge(SILValue dest, Collection collection,
2149+
bool requireOperands = true) {
21402150
auto trackableDest = tryToTrackValue(dest);
21412151
if (!trackableDest)
21422152
return;
21432153
for (SILValue elt : collection) {
21442154
if (auto trackableSrc = tryToTrackValue(elt)) {
2155+
if (requireOperands) {
2156+
builder.addRequire(trackableSrc->getRepresentative().getValue());
2157+
builder.addRequire(trackableDest->getRepresentative().getValue());
2158+
}
21452159
builder.addMerge(trackableDest->getRepresentative().getValue(),
21462160
trackableSrc->getRepresentative().getValue());
21472161
}
21482162
}
21492163
}
21502164

21512165
template <>
2152-
void translateSILMerge<SILValue>(SILValue dest, SILValue src) {
2153-
return translateSILMerge(dest, TinyPtrVector<SILValue>(src));
2166+
void translateSILMerge<SILValue>(SILValue dest, SILValue src,
2167+
bool requireOperands) {
2168+
return translateSILMerge(dest, TinyPtrVector<SILValue>(src),
2169+
requireOperands);
21542170
}
21552171

21562172
void translateSILAssignmentToTransferringParameter(TrackableValue destRoot,
@@ -2641,7 +2657,6 @@ CONSTANT_TRANSLATION(InitExistentialValueInst, LookThrough)
26412657
CONSTANT_TRANSLATION(CopyAddrInst, Store)
26422658
CONSTANT_TRANSLATION(ExplicitCopyAddrInst, Store)
26432659
CONSTANT_TRANSLATION(StoreInst, Store)
2644-
CONSTANT_TRANSLATION(StoreBorrowInst, Store)
26452660
CONSTANT_TRANSLATION(StoreWeakInst, Store)
26462661
CONSTANT_TRANSLATION(MarkUnresolvedMoveAddrInst, Store)
26472662
CONSTANT_TRANSLATION(UncheckedRefCastAddrInst, Store)
@@ -2895,6 +2910,44 @@ LOOKTHROUGH_IF_NONSENDABLE_RESULT_AND_OPERAND(UncheckedTakeEnumDataAddrInst)
28952910
// Custom Handling
28962911
//
28972912

2913+
TranslationSemantics
2914+
PartitionOpTranslator::visitStoreBorrowInst(StoreBorrowInst *sbi) {
2915+
// A store_borrow is an interesting instruction since we are essentially
2916+
// temporarily binding an object value to an address... so really any uses of
2917+
// the address, we want to consider to be uses of the parent object. So we
2918+
// basically put source/dest into the same region, but do not consider the
2919+
// store_borrow itself to be a require use. This prevents the store_borrow
2920+
// from causing incorrect diagnostics.
2921+
SILValue destValue = sbi->getDest();
2922+
SILValue srcValue = sbi->getSrc();
2923+
2924+
auto nonSendableDest = tryToTrackValue(destValue);
2925+
if (!nonSendableDest)
2926+
return TranslationSemantics::Ignored;
2927+
2928+
// In the following situations, we can perform an assign:
2929+
//
2930+
// 1. A store to unaliased storage.
2931+
// 2. A store that is to an entire value.
2932+
//
2933+
// DISCUSSION: If we have case 2, we need to merge the regions since we
2934+
// are not overwriting the entire region of the value. This does mean that
2935+
// we artificially include the previous region that was stored
2936+
// specifically in this projection... but that is better than
2937+
// miscompiling. For memory like this, we probably need to track it on a
2938+
// per field basis to allow for us to assign.
2939+
if (nonSendableDest.value().isNoAlias() &&
2940+
!isProjectedFromAggregate(destValue)) {
2941+
translateSILMultiAssign(sbi->getResults(), sbi->getOperandValues(),
2942+
SILIsolationInfo(), false /*require src*/);
2943+
} else {
2944+
// Stores to possibly aliased storage must be treated as merges.
2945+
translateSILMerge(destValue, srcValue, false /*require src*/);
2946+
}
2947+
2948+
return TranslationSemantics::Special;
2949+
}
2950+
28982951
TranslationSemantics
28992952
PartitionOpTranslator::visitAllocStackInst(AllocStackInst *asi) {
29002953
// Before we do anything, see if asi is Sendable or if it is non-Sendable,

test/Concurrency/concurrent_value_checking.swift

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
// REQUIRES: asserts
77

88
class NotConcurrent { } // expected-note 13{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
9-
// expected-complete-note @-1 13{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
10-
// expected-tns-allow-typechecker-note @-2 {{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
9+
// expected-tns-allow-typechecker-note @-1 {{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
1110

1211
// ----------------------------------------------------------------------
1312
// Sendable restriction on actor operations
@@ -67,23 +66,23 @@ actor A2 {
6766
}
6867

6968
func testActorCreation(value: NotConcurrent) async {
70-
_ = A2(value: value) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent' into actor-isolated context may introduce data races}}
69+
_ = A2(value: value)
7170
// expected-tns-warning @-1 {{sending 'value' risks causing data races}}
7271
// expected-tns-note @-2 {{sending task-isolated 'value' to actor-isolated initializer 'init(value:)' risks causing data races between actor-isolated and task-isolated uses}}
7372

74-
_ = await A2(valueAsync: value) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent' into actor-isolated context may introduce data races}}
73+
_ = await A2(valueAsync: value)
7574
// expected-tns-warning @-1 {{sending 'value' risks causing data races}}
7675
// expected-tns-note @-2 {{sending task-isolated 'value' to actor-isolated initializer 'init(valueAsync:)' risks causing data races between actor-isolated and task-isolated uses}}
7776

78-
_ = A2(delegatingSync: value) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent' into actor-isolated context may introduce data races}}
77+
_ = A2(delegatingSync: value)
7978
// expected-tns-warning @-1 {{sending 'value' risks causing data races}}
8079
// expected-tns-note @-2 {{sending task-isolated 'value' to actor-isolated initializer 'init(delegatingSync:)' risks causing data races between actor-isolated and task-isolated uses}}
8180

82-
_ = await A2(delegatingAsync: value, 9) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent' into actor-isolated context may introduce data races}}
81+
_ = await A2(delegatingAsync: value, 9)
8382
// expected-tns-warning @-1 {{sending 'value' risks causing data races}}
8483
// expected-tns-note @-2 {{sending task-isolated 'value' to actor-isolated initializer 'init(delegatingAsync:_:)' risks causing data races between actor-isolated and task-isolated uses}}
8584

86-
_ = await A2(nonisoAsync: value, 3) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent' into actor-isolated context may introduce data races}}
85+
_ = await A2(nonisoAsync: value, 3)
8786
// expected-tns-warning @-1 {{sending 'value' risks causing data races}}
8887
// expected-tns-note @-2 {{sending task-isolated 'value' to actor-isolated initializer 'init(nonisoAsync:_:)' risks causing data races between actor-isolated and task-isolated uses}}
8988
}
@@ -104,7 +103,7 @@ extension A1 {
104103
// expected-warning@-1 {{expression is 'async' but is not marked with 'await'}}
105104
// expected-note@-2 {{property access is 'async'}}
106105
_ = await other.synchronous() // expected-warning{{non-sendable type 'NotConcurrent?' returned by call to actor-isolated function cannot cross actor boundary}}
107-
_ = await other.asynchronous(nil) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into actor-isolated context may introduce data races}}
106+
_ = await other.asynchronous(nil)
108107
}
109108
}
110109

@@ -142,8 +141,9 @@ func globalTest() async {
142141
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
143142
// expected-note@+1 {{property access is 'async'}}
144143
let a = globalValue // expected-warning{{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated let 'globalValue' cannot cross actor boundary}}
145-
await globalAsync(a) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
146-
await globalSync(a) // expected-complete-warning{{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
144+
await globalAsync(a) // expected-tns-warning {{sending 'a' risks causing data races}}
145+
// expected-tns-note @-1 {{sending global actor 'SomeGlobalActor'-isolated 'a' to global actor 'SomeGlobalActor'-isolated global function 'globalAsync' risks causing data races between global actor 'SomeGlobalActor'-isolated and local nonisolated uses}}
146+
await globalSync(a) // expected-tns-note {{access can happen concurrently}}
147147

148148
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
149149
// expected-note@+1 {{property access is 'async'}}
@@ -154,7 +154,7 @@ func globalTest() async {
154154
// expected-typechecker-note@+2 {{call is 'async'}}
155155
// expected-typechecker-note@+1 {{property access is 'async'}}
156156
globalAsync(E.notSafe)
157-
// expected-complete-warning@-1 {{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
157+
158158
// expected-typechecker-warning@-2 {{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated static property 'notSafe' cannot cross actor boundary}}
159159
#endif
160160
}
@@ -178,9 +178,10 @@ func globalTestMain(nc: NotConcurrent) async {
178178
// expected-warning@+2 {{expression is 'async' but is not marked with 'await'}}
179179
// expected-note@+1 {{property access is 'async'}}
180180
let a = globalValue // expected-warning {{non-sendable type 'NotConcurrent?' in implicitly asynchronous access to global actor 'SomeGlobalActor'-isolated let 'globalValue' cannot cross actor boundary}}
181-
await globalAsync(a) // expected-complete-warning {{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
182-
await globalSync(a) // expected-complete-warning {{passing argument of non-sendable type 'NotConcurrent?' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
183-
_ = await ClassWithGlobalActorInits(nc) // expected-complete-warning {{passing argument of non-sendable type 'NotConcurrent' into global actor 'SomeGlobalActor'-isolated context may introduce data races}}
181+
await globalAsync(a) // expected-tns-warning {{sending 'a' risks causing data races}}
182+
// expected-tns-note @-1 {{sending global actor 'SomeGlobalActor'-isolated 'a' to global actor 'SomeGlobalActor'-isolated global function 'globalAsync' risks causing data races between global actor 'SomeGlobalActor'-isolated and local main actor-isolated uses}}
183+
await globalSync(a) // expected-tns-note {{access can happen concurrently}}
184+
_ = await ClassWithGlobalActorInits(nc)
184185
// expected-warning @-1 {{non-sendable type 'ClassWithGlobalActorInits' returned by call to global actor 'SomeGlobalActor'-isolated function cannot cross actor boundary}}
185186
// expected-tns-warning @-2 {{sending 'nc' risks causing data races}}
186187
// expected-tns-note @-3 {{sending main actor-isolated 'nc' to global actor 'SomeGlobalActor'-isolated initializer 'init(_:)' risks causing data races between global actor 'SomeGlobalActor'-isolated and main actor-isolated uses}}

test/Concurrency/sendable_checking.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,12 @@ func testNonSendableBaseArg() async {
295295
let t = NonSendable()
296296
await t.update()
297297
// expected-targeted-and-complete-warning @-1 {{passing argument of non-sendable type 'NonSendable' into main actor-isolated context may introduce data races}}
298+
// expected-tns-warning @-2 {{sending 't' risks causing data races}}
299+
// expected-tns-note @-3 {{sending 't' to main actor-isolated instance method 'update()' risks causing data races between main actor-isolated and local nonisolated uses}}
298300

299301
_ = await t.x
300302
// expected-warning @-1 {{non-sendable type 'NonSendable' passed in implicitly asynchronous call to main actor-isolated property 'x' cannot cross actor boundary}}
303+
// expected-tns-note @-2 {{access can happen concurrently}}
301304
}
302305

303306
// We get the region isolation error here since t.y is custom actor isolated.

test/Concurrency/transfernonsendable.swift

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
////////////////////////
1515

1616
/// Classes are always non-sendable, so this is non-sendable
17-
class NonSendableKlass { // expected-complete-note 51{{}}
17+
class NonSendableKlass { // expected-complete-note 53{{}}
1818
// expected-typechecker-only-note @-1 3{{}}
1919
// expected-tns-note @-2 {{}}
2020
var field: NonSendableKlass? = nil
@@ -145,10 +145,7 @@ func closureInOut(_ a: MyActor) async {
145145
// expected-tns-note @-3 {{sending 'ns0' to actor-isolated instance method 'useKlass' risks causing data races between actor-isolated and local nonisolated uses}}
146146

147147
if await booleanFlag {
148-
// This is not an actual use since we are passing values to the same
149-
// isolation domain.
150-
await a.useKlass(ns1)
151-
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableKlass'}}
148+
await a.useKlass(ns1) // expected-tns-note {{access can happen concurrently}}
152149
} else {
153150
closure() // expected-tns-note {{access can happen concurrently}}
154151
}
@@ -1224,11 +1221,11 @@ func varNonSendableNonTrivialLetStructFieldClosureFlowSensitive4() async {
12241221
// good... that is QoI though.
12251222
await transferToMain(test) // expected-tns-warning {{sending 'test' risks causing data races}}
12261223
// expected-tns-note @-1 {{sending 'test' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
1227-
// expected-complete-warning @-2 {{passing argument of non-sendable type 'StructFieldTests' into main actor-isolated context may introduce data races}}
1224+
// expected-tns-note @-2 {{access can happen concurrently}}
12281225

12291226
// This is treated as a use since test is in box form and is mutable. So we
12301227
// treat assignment as a merge.
1231-
test = StructFieldTests() // expected-tns-note {{access can happen concurrently}}
1228+
test = StructFieldTests()
12321229
cls = {
12331230
useInOut(&test.varSendableNonTrivial)
12341231
}
@@ -1259,7 +1256,7 @@ func varNonSendableNonTrivialLetStructFieldClosureFlowSensitive5() async {
12591256
}
12601257

12611258
test.varSendableNonTrivial = SendableKlass()
1262-
useValue(test) // expected-tns-note {{access can happen concurrently}}
1259+
useValue(test)
12631260
}
12641261

12651262
func varNonSendableNonTrivialLetStructFieldClosureFlowSensitive6() async {
@@ -1428,7 +1425,7 @@ func controlFlowTest2() async {
14281425
x = NonSendableKlass()
14291426
}
14301427

1431-
useValue(x) // expected-tns-note {{access can happen concurrently}}
1428+
useValue(x)
14321429
}
14331430

14341431
////////////////////////
@@ -1738,6 +1735,17 @@ func sendableGlobalActorIsolated() {
17381735
print(x) // expected-tns-note {{access can happen concurrently}}
17391736
}
17401737

1738+
// We do not get an error here since we are transferring x both times to a main
1739+
// actor isolated thing function. We used to emit an error when using region
1740+
// isolation since we would trip on the store_borrow we used to materialize the
1741+
// value.
1742+
func testIndirectParameterSameIsolationNoError() async {
1743+
let x = NonSendableKlass()
1744+
await transferToMain(x) // expected-tns-warning {{sending 'x' risks causing data races}}
1745+
// expected-tns-note @-1 {{sending 'x' to main actor-isolated global function 'transferToMain' risks causing data races between main actor-isolated and local nonisolated uses}}
1746+
await transferToMain(x) // expected-tns-note {{access can happen concurrently}}
1747+
}
1748+
17411749
extension MyActor {
17421750
func testNonSendableCaptures(sc: NonSendableKlass) {
17431751
Task {

0 commit comments

Comments
 (0)