Skip to content

Commit 2e5b3bc

Browse files
committed
[region-isolation] Do not treat functions/class_methods that are isolated to a #isolated as being globally isolated to that.
Instead, we need to consider the isolation at the apply site. rdar://126285681 rdar://125078448
1 parent a9c163f commit 2e5b3bc

File tree

6 files changed

+146
-45
lines changed

6 files changed

+146
-45
lines changed

include/swift/SILOptimizer/Analysis/RegionAnalysis.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ class regionanalysisimpl::TrackableValueState {
186186
<< "][is_no_alias: " << (isNoAlias() ? "yes" : "no")
187187
<< "][is_sendable: " << (isSendable() ? "yes" : "no")
188188
<< "][region_value_kind: ";
189-
getIsolationRegionInfo().print(os);
189+
getIsolationRegionInfo().printForDiagnostics(os);
190190
os << "].";
191191
}
192192

include/swift/SILOptimizer/Utils/PartitionUtils.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,11 @@ class SILIsolationInfo {
161161

162162
void printForDiagnostics(llvm::raw_ostream &os) const;
163163

164+
SWIFT_DEBUG_DUMPER(dumpForDiagnostics()) {
165+
printForDiagnostics(llvm::dbgs());
166+
llvm::dbgs() << '\n';
167+
}
168+
164169
ActorIsolation getActorIsolation() const {
165170
assert(kind == Actor);
166171
return actorIsolation;

lib/SILOptimizer/Utils/PartitionUtils.cpp

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include "llvm/Support/CommandLine.h"
2323

24+
#pragma clang optimize off
25+
2426
using namespace swift;
2527
using namespace swift::PatternMatch;
2628
using namespace swift::PartitionPrimitives;
@@ -71,14 +73,6 @@ getGlobalActorInitIsolation(SILFunction *fn) {
7173
}
7274

7375
SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
74-
if (ApplyExpr *apply = inst->getLoc().getAsASTNode<ApplyExpr>()) {
75-
if (auto crossing = apply->getIsolationCrossing()) {
76-
if (crossing->getCalleeIsolation().isActorIsolated())
77-
return SILIsolationInfo::getActorIsolated(
78-
SILValue(), SILValue(), crossing->getCalleeIsolation());
79-
}
80-
}
81-
8276
if (auto fas = FullApplySite::isa(inst)) {
8377
if (auto crossing = fas.getIsolationCrossing()) {
8478
if (crossing->getCalleeIsolation().isActorIsolated()) {
@@ -149,7 +143,9 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
149143
// Treat function ref as either actor isolated or sendable.
150144
if (auto *fri = dyn_cast<FunctionRefInst>(inst)) {
151145
auto isolation = fri->getReferencedFunction()->getActorIsolation();
152-
if (isolation.isActorIsolated()) {
146+
if (isolation.isActorIsolated() &&
147+
(isolation.getKind() != ActorIsolation::ActorInstance ||
148+
isolation.getActorInstanceParameter() == 0)) {
153149
return SILIsolationInfo::getActorIsolated(fri, SILValue(), isolation);
154150
}
155151

@@ -178,11 +174,15 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
178174
}
179175

180176
if (auto *cmi = dyn_cast<ClassMethodInst>(inst)) {
177+
// Ok, we know that we do not have an actor... but we might have a global
178+
// actor isolated method. Use the AST to compute the actor isolation and
179+
// check if we are self. If we are not self, we want this to be
180+
// disconnected.
181181
if (auto *declRefExpr = cmi->getLoc().getAsASTNode<DeclRefExpr>()) {
182-
// See if we are actor isolated. If so, treat this as non-Sendable so we
183-
// propagate actor isolation.
184182
if (auto isolation = swift::getActorIsolation(declRefExpr->getDecl())) {
185-
if (isolation.isActorIsolated()) {
183+
if (isolation.isActorIsolated() &&
184+
(isolation.getKind() != ActorIsolation::ActorInstance ||
185+
isolation.getActorInstanceParameter() == 0)) {
186186
auto actor = cmi->getOperand()->getType().isActor()
187187
? cmi->getOperand()
188188
: SILValue();
@@ -264,6 +264,16 @@ SILIsolationInfo SILIsolationInfo::get(SILInstruction *inst) {
264264
}
265265
}
266266

267+
// Try to infer using SIL first since we might be able to get the source name
268+
// of the actor.
269+
if (ApplyExpr *apply = inst->getLoc().getAsASTNode<ApplyExpr>()) {
270+
if (auto crossing = apply->getIsolationCrossing()) {
271+
if (crossing->getCalleeIsolation().isActorIsolated())
272+
return SILIsolationInfo::getActorIsolated(
273+
SILValue(), SILValue(), crossing->getCalleeIsolation());
274+
}
275+
}
276+
267277
return SILIsolationInfo();
268278
}
269279

test/Concurrency/isolated_parameters.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
// REQUIRES: concurrency
55
// REQUIRES: swift_swift_parser
6-
// REQUIRES: rdar126006489
76

87
@available(SwiftStdlib 5.1, *)
98
actor A {

test/Concurrency/transfernonsendable.swift

Lines changed: 63 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,19 @@
1515
////////////////////////
1616

1717
/// Classes are always non-sendable, so this is non-sendable
18-
class NonSendableKlass { // expected-complete-note 41{{}}
18+
class NonSendableKlass { // expected-complete-note 45{{}}
1919
// expected-typechecker-only-note @-1 4{{}}
2020
// expected-tns-note @-2 2{{}}
2121
var field: NonSendableKlass? = nil
2222

2323
func asyncCall() async {}
24+
func asyncCallWithIsolatedParameter(isolation: isolated (any Actor)? = #isolation) async {
25+
}
2426
}
2527

2628
class SendableKlass : @unchecked Sendable {}
2729

28-
actor Actor {
30+
actor MyActor {
2931
var klass = NonSendableKlass()
3032
// expected-typechecker-only-note @-1 7{{property declared here}}
3133
final var finalKlass = NonSendableKlass()
@@ -79,7 +81,7 @@ class TwoFieldKlassClassBox {
7981
// MARK: Actor Self Tests //
8082
////////////////////////////
8183

82-
extension Actor {
84+
extension MyActor {
8385
func warningIfCallingGetter() async {
8486
await self.klass.asyncCall() // expected-complete-warning {{passing argument of non-sendable type 'NonSendableKlass' outside of actor-isolated context may introduce data races}}
8587
// expected-tns-warning @-1 {{transferring 'self.klass' may cause a data race}}
@@ -120,7 +122,7 @@ func formClosureWithoutCrashing() {
120122

121123
// In this test, closure is not promoted into its box form. As a result, we
122124
// treat assignments into contents as a merge operation rather than an assign.
123-
func closureInOut(_ a: Actor) async {
125+
func closureInOut(_ a: MyActor) async {
124126
var contents = NonSendableKlass()
125127
let ns0 = NonSendableKlass()
126128
let ns1 = NonSendableKlass()
@@ -146,7 +148,7 @@ func closureInOut(_ a: Actor) async {
146148
}
147149
}
148150

149-
func closureInOutDifferentActor(_ a: Actor, _ a2: Actor) async {
151+
func closureInOutDifferentActor(_ a: MyActor, _ a2: MyActor) async {
150152
var contents = NonSendableKlass()
151153
let ns0 = NonSendableKlass()
152154
let ns1 = NonSendableKlass()
@@ -163,18 +165,17 @@ func closureInOutDifferentActor(_ a: Actor, _ a2: Actor) async {
163165
// expected-tns-note @-3 {{transferring disconnected 'ns0' to actor-isolated callee could cause races in between callee actor-isolated and local nonisolated uses}}
164166

165167
// We only emit a warning on the first use we see, so make sure we do both
166-
// the print and the closure.
168+
// the use and the closure.
167169
if await booleanFlag {
168-
// This is an actual use since a2 is a different actor from a1
169-
await a2.useKlass(ns1)
170+
await a2.useKlass(ns1) // expected-tns-note {{use here could race}}
170171
// expected-complete-warning @-1 {{passing argument of non-sendable type 'NonSendableKlass'}}
171172
} else {
172173
closure() // expected-tns-note {{use here could race}}
173174
}
174175
}
175176

176177

177-
func closureInOut2(_ a: Actor) async {
178+
func closureInOut2(_ a: MyActor) async {
178179
var contents = NonSendableKlass()
179180
let ns0 = NonSendableKlass()
180181
let ns1 = NonSendableKlass()
@@ -197,7 +198,7 @@ func closureInOut2(_ a: Actor) async {
197198
closure() // expected-tns-note {{use here could race}}
198199
}
199200

200-
func closureNonInOut(_ a: Actor) async {
201+
func closureNonInOut(_ a: MyActor) async {
201202
var contents = NonSendableKlass()
202203
let ns0 = NonSendableKlass()
203204
let ns1 = NonSendableKlass()
@@ -224,7 +225,7 @@ func closureNonInOut(_ a: Actor) async {
224225
// method, we cannot access the inside of the actor without using an await, so
225226
// this is safe.
226227
func transferNonIsolatedNonAsyncClosureTwice() async {
227-
let a = Actor()
228+
let a = MyActor()
228229

229230
// This is non-isolated and non-async... we can transfer it safely.
230231
var actorCaptureClosure = { print(a) }
@@ -236,7 +237,7 @@ func transferNonIsolatedNonAsyncClosureTwice() async {
236237
// expected-complete-note @-1 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
237238
}
238239

239-
extension Actor {
240+
extension MyActor {
240241
// Simple just capture self and access field.
241242
func simpleClosureCaptureSelfAndTransfer() async {
242243
let closure: () -> () = {
@@ -428,7 +429,7 @@ extension Actor {
428429
}
429430

430431
func testSimpleLetClosureCaptureActor() async {
431-
let a = Actor()
432+
let a = MyActor()
432433
let closure = { print(a) }
433434
await transferToMain(closure) // expected-complete-warning {{passing argument of non-sendable type '() -> ()' into main actor-isolated context may introduce data races}}
434435
// expected-complete-note @-1 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
@@ -437,23 +438,23 @@ func testSimpleLetClosureCaptureActor() async {
437438
#if TYPECHECKER_ONLY
438439

439440
func testSimpleLetClosureCaptureActorField() async {
440-
let a = Actor()
441+
let a = MyActor()
441442
let closure = { print(a.klass) } // expected-typechecker-only-error {{actor-isolated property 'klass' can not be referenced from a non-isolated context}}
442443
await transferToMain(closure)
443444
// expected-complete-warning @-1 {{passing argument of non-sendable type '() -> ()' into main actor-isolated context may introduce data races}}
444445
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
445446
}
446447

447448
func testSimpleLetClosureCaptureActorFieldThroughTuple() async {
448-
let a = (Actor(), 0)
449+
let a = (MyActor(), 0)
449450
let closure = { print(a.0.klass) } // expected-typechecker-only-error {{actor-isolated property 'klass' can not be referenced from a non-isolated context}}
450451
await transferToMain(closure)
451452
// expected-complete-warning @-1 {{passing argument of non-sendable type '() -> ()' into main actor-isolated context may introduce data races}}
452453
// expected-complete-note @-2 {{a function type must be marked '@Sendable' to conform to 'Sendable'}}
453454
}
454455

455456
func testSimpleLetClosureCaptureActorFieldThroughOptional() async {
456-
let a: Actor? = Actor()
457+
let a: MyActor? = MyActor()
457458
let closure = { print(a!.klass) } // expected-typechecker-only-error {{actor-isolated property 'klass' can not be referenced from a non-isolated context}}
458459
await transferToMain(closure)
459460
// expected-complete-warning @-1 {{passing argument of non-sendable type '() -> ()' into main actor-isolated context may introduce data races}}
@@ -463,7 +464,7 @@ func testSimpleLetClosureCaptureActorFieldThroughOptional() async {
463464
#endif
464465

465466
func testSimpleVarClosureCaptureActor() async {
466-
let a = Actor()
467+
let a = MyActor()
467468
var closure = {}
468469
closure = { print(a) }
469470
await transferToMain(closure) // expected-complete-warning {{passing argument of non-sendable type '() -> ()' into main actor-isolated context may introduce data races}}
@@ -473,7 +474,7 @@ func testSimpleVarClosureCaptureActor() async {
473474
#if TYPECHECKER_ONLY
474475

475476
func testSimpleVarClosureCaptureActorField() async {
476-
let a = Actor()
477+
let a = MyActor()
477478
var closure = {}
478479
closure = { print(a.klass) } // expected-typechecker-only-error {{actor-isolated property 'klass' can not be referenced from a non-isolated context}}
479480
await transferToMain(closure)
@@ -482,7 +483,7 @@ func testSimpleVarClosureCaptureActorField() async {
482483
}
483484

484485
func testSimpleVarClosureCaptureActorFieldThroughTuple() async {
485-
let a = (Actor(), 0)
486+
let a = (MyActor(), 0)
486487
var closure = {}
487488
closure = { print(a.0.klass) } // expected-typechecker-only-error {{actor-isolated property 'klass' can not be referenced from a non-isolated context}}
488489
await transferToMain(closure)
@@ -491,7 +492,7 @@ func testSimpleVarClosureCaptureActorFieldThroughTuple() async {
491492
}
492493

493494
func testSimpleVarClosureCaptureActorFieldThroughOptional() async {
494-
let a: Actor? = Actor()
495+
let a: MyActor? = MyActor()
495496
var closure = {}
496497
closure = { print(a!.klass) } // expected-typechecker-only-error {{actor-isolated property 'klass' can not be referenced from a non-isolated context}}
497498
await transferToMain(closure)
@@ -501,7 +502,7 @@ func testSimpleVarClosureCaptureActorFieldThroughOptional() async {
501502

502503
#endif
503504

504-
extension Actor {
505+
extension MyActor {
505506
// Make sure that we properly propagate actor derived from klass into field's
506507
// value.
507508
func simplePropagateNonSendableThroughMultipleProperty() async {
@@ -529,7 +530,7 @@ extension Actor {
529530
}
530531
}
531532

532-
extension Actor {
533+
extension MyActor {
533534
func testVarReassignStopActorDerived() async {
534535
var closure: () -> () = {
535536
print(self)
@@ -566,7 +567,7 @@ extension Actor {
566567

567568
// Make sure that we do not error on function values that are Sendable... even
568569
// if the function is converted to a non-Sendable form by a function conversion.
569-
func testConversionsAndSendable(a: Actor, f: @Sendable () -> Void, f2: () -> Void) async {
570+
func testConversionsAndSendable(a: MyActor, f: @Sendable () -> Void, f2: () -> Void) async {
570571
// No function conversion.
571572
await a.useSendableFunction(f)
572573

@@ -580,7 +581,7 @@ func testConversionsAndSendable(a: Actor, f: @Sendable () -> Void, f2: () -> Voi
580581
// expected-tns-note @-3 {{transferring task-isolated 'f2' to actor-isolated callee could cause races between actor-isolated and task-isolated uses}}
581582
}
582583

583-
func testSendableClosureCapturesNonSendable(a: Actor) {
584+
func testSendableClosureCapturesNonSendable(a: MyActor) {
584585
let klass = NonSendableKlass()
585586
let _ = { @Sendable in
586587
_ = klass // expected-warning {{capture of 'klass' with non-sendable type 'NonSendableKlass' in a `@Sendable` closure}}
@@ -1572,10 +1573,47 @@ func functionArgumentIntoClosure(_ x: @escaping () -> ()) async {
15721573
}
15731574

15741575
func testGetActorName() async {
1575-
let a = Actor()
1576+
let a = MyActor()
15761577
let x = NonSendableKlass()
15771578
await a.useKlass(x) // expected-tns-warning {{transferring 'x' may cause a data race}}
15781579
// expected-tns-note @-1 {{transferring disconnected 'x' to actor-isolated callee could cause races in between callee actor-isolated and local nonisolated uses}}
15791580
// expected-complete-warning @-2 {{passing argument of non-sendable type 'NonSendableKlass' into actor-isolated context may introduce data races}}
15801581
useValue(x) // expected-tns-note {{use here could race}}
15811582
}
1583+
1584+
extension MyActor {
1585+
func testCallBangIsolatedMethod(other: MyActor) async {
1586+
await klass.asyncCallWithIsolatedParameter(isolation: other) // expected-tns-warning {{transferring 'self.klass' may cause a data race}}
1587+
// expected-tns-note @-1 {{transferring 'self'-isolated 'self.klass' to actor-isolated callee could cause races between actor-isolated and 'self'-isolated uses}}
1588+
// expected-complete-warning @-2 {{passing argument of non-sendable type 'NonSendableKlass' into actor-isolated context may introduce data races}}
1589+
}
1590+
}
1591+
1592+
extension FinalActor {
1593+
func testCallBangIsolatedMethod(other: MyActor) async {
1594+
await klass.asyncCallWithIsolatedParameter(isolation: other) // expected-tns-warning {{transferring 'self.klass' may cause a data race}}
1595+
// expected-tns-note @-1 {{transferring 'self'-isolated 'self.klass' to actor-isolated callee could cause races between actor-isolated and 'self'-isolated uses}}
1596+
// expected-complete-warning @-2 {{passing argument of non-sendable type 'NonSendableKlass' into actor-isolated context may introduce data races}}
1597+
}
1598+
}
1599+
1600+
extension NonSendableKlass {
1601+
func directAsyncCallWithIsolatedParameter(isolation: isolated (any Actor)? = #isolation) async {
1602+
}
1603+
}
1604+
1605+
extension MyActor {
1606+
func testCallBangIsolatedDirectMethod(other: MyActor) async {
1607+
await klass.directAsyncCallWithIsolatedParameter(isolation: other) // expected-tns-warning {{transferring 'self.klass' may cause a data race}}
1608+
// expected-tns-note @-1 {{transferring 'self'-isolated 'self.klass' to actor-isolated callee could cause races between actor-isolated and 'self'-isolated uses}}
1609+
// expected-complete-warning @-2 {{passing argument of non-sendable type 'NonSendableKlass' into actor-isolated context may introduce data races}}
1610+
}
1611+
}
1612+
1613+
extension FinalActor {
1614+
func testCallBangIsolatedDirectMethod(other: MyActor) async {
1615+
await klass.directAsyncCallWithIsolatedParameter(isolation: other) // expected-tns-warning {{transferring 'self.klass' may cause a data race}}
1616+
// expected-tns-note @-1 {{transferring 'self'-isolated 'self.klass' to actor-isolated callee could cause races between actor-isolated and 'self'-isolated uses}}
1617+
// expected-complete-warning @-2 {{passing argument of non-sendable type 'NonSendableKlass' into actor-isolated context may introduce data races}}
1618+
}
1619+
}

0 commit comments

Comments
 (0)