You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[region-isolation] Assigns to var with structs/tuples with multiple fields should be merges for now.
The reason that this is being done is that since currently region based
isolation is not field sensitive, when we assign to the struct or tuple field of
the var, the new region relationship is set for the entire struct, not just a
specific field. This means that we effectively lose any region information from
the other fields. For example in the following at (1), given the current rules, we
lose that s.y was assigned to x:
```swift
struct S {
var x: NonSendableKlass
var y: NonSendableKlass
}
func foo() {
var s = S()
// Regions: [s]
let x = NonSendableKlass()
let y = NonSendableKlass()
// Regions: [(s), (x), (y)]
s.y = x
// Regions: [(s, x), (y)]
s.x = y (1)
// Regions: [(x), (s, y)]
}
```
The best solution to this is to track such var like bindings in a field
sensitive manner where the regions of the aggregate are the union of the
individual fields. This would let us represent the regions of the above as
follows:
```swift
func foo() {
var s = S()
// Regions: [((s.x), (s.y))]
let x = NonSendableKlass()
let y = NonSendableKlass()
// Regions: [((s.x), (s.y)), (x), (y)]
s.y = x
// Regions: [((s.x), (s.y, x)), (y)]
s.x = y (1)
// Regions: [((s.x, y), (s.y, x))]
}
```
We cannot do this today so to plug this hole, we instead treat these operations
as merges. This provides a conservative answer. Thus we would have:"
```swift
func foo() {
var s = S()
// Regions: [s]
let x = NonSendableKlass()
let y = NonSendableKlass()
// Regions: [(s), (x), (y)]
s.y = x
// Regions: [(s, x), (y)]
s.x = y (1)
// Regions: [(s, x, y])
}
```
This is because we are treating the assignment into s.y and s.x as merges into
s, so we do not lose that y was assigned into s before we assigned y into
it. The unfortunate side-effect of this is that if a struct or tuple has
multiple fields, the merge makes it so that if we assign over the same field, we
do not lose the region of the old value:
```swift
func foo() {
var s = S()
// Regions: [s]
let x = NonSendableKlass()
let y = NonSendableKlass()
// Regions: [(s), (x), (y)]
s.y = x
// Regions: [(s, x), (y)]
s.y = y (1)
// Regions: [(s, x, y])
}
```
If we were not to do this, then the s.y at (1) would result in s and x being in
different regions.
rdar://117273952
Copy file name to clipboardExpand all lines: test/Concurrency/sendnonsendable_basic.swift
+115-3Lines changed: 115 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -44,6 +44,15 @@ func useValue<T>(_ x: T) {}
44
44
45
45
varbooleanFlag:Bool{false}
46
46
47
+
structSingleFieldKlassBox{
48
+
vark=NonSendableKlass()
49
+
}
50
+
51
+
structTwoFieldKlassBox{
52
+
vark1=NonSendableKlass()
53
+
vark2=NonSendableKlass()
54
+
}
55
+
47
56
////////////////////////////
48
57
// MARK: Actor Self Tests //
49
58
////////////////////////////
@@ -187,10 +196,9 @@ extension Actor {
187
196
letclosure:()->()={
188
197
print(self.klass)
189
198
}
190
-
// NOTE: We do not error on this today since we assign into 1 and that makes
191
-
// x assign fresh. It will be fixed in a forthcoming commit.
199
+
192
200
letx=(closure,1)
193
-
awaittransferToMain(x)
201
+
awaittransferToMain(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}}
194
202
// expected-complete-warning @-1 {{passing argument of non-sendable type '(() -> (), Int)' into main actor-isolated context may introduce data races}}
195
203
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
// expected-complete-warning @-1 {{passing argument of non-sendable type '() -> Void' into actor-isolated context may introduce data races}}
492
500
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
493
501
}
502
+
503
+
///////////////////////////////////////////////
504
+
// Multiple Field Var Assignment Merge Tests //
505
+
///////////////////////////////////////////////
506
+
507
+
func singleFieldVarMergeTest()async{
508
+
varbox=SingleFieldKlassBox()
509
+
box =SingleFieldKlassBox()
510
+
511
+
// This transfers the entire region.
512
+
awaittransferToMain(box.k)
513
+
514
+
// But since box has only a single element, this doesn't race.
515
+
box.k =NonSendableKlass()
516
+
useValue(box.k)
517
+
useValue(box)
518
+
519
+
// We transfer the box back to main.
520
+
awaittransferToMain(box)
521
+
522
+
// And reassign over the entire box, so again we can use it again.
523
+
box =SingleFieldKlassBox()
524
+
525
+
useValue(box)
526
+
useValue(box.k)
527
+
528
+
awaittransferToMain(box) // expected-tns-warning {{passing argument of non-sendable type 'SingleFieldKlassBox' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}}
529
+
530
+
// But if we use box.k here, we emit an error since we didn't reinitialize at
531
+
// all.
532
+
useValue(box.k) // expected-note {{access here could race}}
533
+
}
534
+
535
+
func multipleFieldVarMergeTest1()async{
536
+
varbox=TwoFieldKlassBox()
537
+
box =TwoFieldKlassBox()
538
+
539
+
// This transfers the entire region.
540
+
awaittransferToMain(box.k1) // expected-tns-warning {{passing argument of non-sendable type 'NonSendableKlass' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}}
541
+
542
+
// So even if we reassign over k1, since we did a merge, this should error.
543
+
box.k1 =NonSendableKlass() // expected-note {{access here could race}}
544
+
useValue(box) // expected-note {{access here could race}}
545
+
}
546
+
547
+
func multipleFieldVarMergeTest2()async{
548
+
varbox=TwoFieldKlassBox()
549
+
box =TwoFieldKlassBox()
550
+
551
+
// This transfers the entire region.
552
+
awaittransferToMain(box.k1)
553
+
554
+
// But if we assign over box completely, we can use it again.
555
+
box =TwoFieldKlassBox()
556
+
557
+
useValue(box.k1)
558
+
useValue(box.k2)
559
+
useValue(box)
560
+
561
+
awaittransferToMain(box.k2)
562
+
563
+
// But if we assign over box completely, we can use it again.
564
+
box =TwoFieldKlassBox()
565
+
566
+
useValue(box.k1)
567
+
useValue(box.k2)
568
+
useValue(box)
569
+
}
570
+
571
+
func multipleFieldTupleMergeTest1()async{
572
+
varbox=(NonSendableKlass(),NonSendableKlass())
573
+
box =(NonSendableKlass(),NonSendableKlass())
574
+
575
+
// This transfers the entire region.
576
+
awaittransferToMain(box.0) // expected-tns-warning {{passing argument of non-sendable type 'NonSendableKlass' from nonisolated context to main actor-isolated context at this call site could yield a race with accesses later in this function}}
577
+
578
+
// So even if we reassign over k1, since we did a merge, this should error.
579
+
box.0=NonSendableKlass() // expected-note {{access here could race}}
580
+
useValue(box) // expected-note {{access here could race}}
581
+
}
582
+
583
+
func multipleFieldTupleMergeTest2()async{
584
+
varbox=(NonSendableKlass(),NonSendableKlass())
585
+
586
+
// This transfers the entire region.
587
+
awaittransferToMain(box.0)
588
+
589
+
letbox2=(NonSendableKlass(),NonSendableKlass())
590
+
// But if we assign over box completely, we can use it again.
591
+
box = box2
592
+
593
+
useValue(box.0)
594
+
useValue(box.1)
595
+
useValue(box)
596
+
597
+
awaittransferToMain(box.1)
598
+
599
+
// But if we assign over box completely, we can use it again.
0 commit comments