Skip to content

Commit 03a9292

Browse files
authored
Merge pull request #75080 from gottesmm/release/6.0-rdar130151318
[6.0][region-isolation] Treat async let as a isolation inference boundary closure and fix diagnostics due to change.
2 parents 112d963 + 12b6975 commit 03a9292

File tree

7 files changed

+234
-27
lines changed

7 files changed

+234
-27
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,10 @@ NOTE(regionbasedisolation_named_isolated_closure_yields_race, none,
10221022
"%0%1 is captured by a %2 closure. %2 uses in closure may race against later %3 uses",
10231023
(StringRef, Identifier, ActorIsolation, ActorIsolation))
10241024

1025+
NOTE(regionbasedisolation_named_transfer_nt_asynclet_capture, none,
1026+
"sending %1 %0 into async let risks causing data races between nonisolated and %1 uses",
1027+
(Identifier, StringRef))
1028+
10251029
// Misc Error.
10261030
ERROR(regionbasedisolation_task_or_actor_isolated_transferred, none,
10271031
"task or actor isolated value cannot be sent", ())

lib/SILOptimizer/Mandatory/TransferNonSendable.cpp

Lines changed: 113 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ getTransferringApplyCalleeInfo(SILInstruction *inst) {
110110
return {};
111111

112112
auto *decl = declRef->getDecl();
113-
if (!decl->hasName())
113+
if (!decl || !decl->hasName())
114114
return {};
115115

116116
return {{decl->getDescriptiveKind(), decl->getName()}};
@@ -1216,6 +1216,10 @@ class TransferNonTransferrableDiagnosticEmitter {
12161216
return getTransferringApplyCalleeInfo(info.transferredOperand->getUser());
12171217
}
12181218

1219+
SILLocation getLoc() const {
1220+
return info.transferredOperand->getUser()->getLoc();
1221+
}
1222+
12191223
/// Return the isolation region info for \p getNonTransferrableValue().
12201224
SILDynamicMergedIsolationInfo getIsolationRegionInfo() const {
12211225
return info.isolationRegionInfo;
@@ -1291,6 +1295,24 @@ class TransferNonTransferrableDiagnosticEmitter {
12911295
.limitBehaviorIf(getBehaviorLimit());
12921296
}
12931297

1298+
void emitNamedAsyncLetCapture(SILLocation loc, Identifier name,
1299+
SILIsolationInfo transferredValueIsolation) {
1300+
assert(!getIsolationRegionInfo().isDisconnected() &&
1301+
"Should never be disconnected?!");
1302+
emitNamedOnlyError(loc, name);
1303+
1304+
SmallString<64> descriptiveKindStr;
1305+
{
1306+
llvm::raw_svector_ostream os(descriptiveKindStr);
1307+
getIsolationRegionInfo().printForDiagnostics(os);
1308+
}
1309+
1310+
diagnoseNote(loc,
1311+
diag::regionbasedisolation_named_transfer_nt_asynclet_capture,
1312+
name, descriptiveKindStr)
1313+
.limitBehaviorIf(getBehaviorLimit());
1314+
}
1315+
12941316
void emitNamedIsolation(SILLocation loc, Identifier name,
12951317
ApplyIsolationCrossing isolationCrossing) {
12961318
emitNamedOnlyError(loc, name);
@@ -1401,6 +1423,8 @@ class TransferNonTransferrableDiagnosticEmitter {
14011423
};
14021424

14031425
class TransferNonTransferrableDiagnosticInferrer {
1426+
struct AutoClosureWalker;
1427+
14041428
TransferNonTransferrableDiagnosticEmitter diagnosticEmitter;
14051429

14061430
public:
@@ -1453,6 +1477,74 @@ bool TransferNonTransferrableDiagnosticInferrer::initForIsolatedPartialApply(
14531477
return false;
14541478
}
14551479

1480+
/// This walker visits an AutoClosureExpr and looks for uses of a specific
1481+
/// captured value. We want to error on the uses in the autoclosure.
1482+
struct TransferNonTransferrableDiagnosticInferrer::AutoClosureWalker
1483+
: ASTWalker {
1484+
TransferNonTransferrableDiagnosticEmitter &foundTypeInfo;
1485+
ValueDecl *targetDecl;
1486+
SILIsolationInfo targetDeclIsolationInfo;
1487+
SmallPtrSet<Expr *, 8> visitedCallExprDeclRefExprs;
1488+
SILLocation captureLoc;
1489+
bool isAsyncLet;
1490+
1491+
AutoClosureWalker(TransferNonTransferrableDiagnosticEmitter &foundTypeInfo,
1492+
ValueDecl *targetDecl,
1493+
SILIsolationInfo targetDeclIsolationInfo,
1494+
SILLocation captureLoc, bool isAsyncLet)
1495+
: foundTypeInfo(foundTypeInfo), targetDecl(targetDecl),
1496+
targetDeclIsolationInfo(targetDeclIsolationInfo),
1497+
captureLoc(captureLoc), isAsyncLet(isAsyncLet) {}
1498+
1499+
Expr *lookThroughArgExpr(Expr *expr) {
1500+
while (true) {
1501+
if (auto *memberRefExpr = dyn_cast<MemberRefExpr>(expr)) {
1502+
expr = memberRefExpr->getBase();
1503+
continue;
1504+
}
1505+
1506+
if (auto *cvt = dyn_cast<ImplicitConversionExpr>(expr)) {
1507+
expr = cvt->getSubExpr();
1508+
continue;
1509+
}
1510+
1511+
if (auto *e = dyn_cast<ForceValueExpr>(expr)) {
1512+
expr = e->getSubExpr();
1513+
continue;
1514+
}
1515+
1516+
if (auto *t = dyn_cast<TupleElementExpr>(expr)) {
1517+
expr = t->getBase();
1518+
continue;
1519+
}
1520+
1521+
return expr;
1522+
}
1523+
}
1524+
1525+
PreWalkResult<Expr *> walkToExprPre(Expr *expr) override {
1526+
if (auto *declRef = dyn_cast<DeclRefExpr>(expr)) {
1527+
// If this decl ref expr was not visited as part of a callExpr and is our
1528+
// target decl... emit a simple async let error.
1529+
//
1530+
// This occurs if we do:
1531+
//
1532+
// ```
1533+
// let x = ...
1534+
// async let y = x
1535+
// ```
1536+
if (declRef->getDecl() == targetDecl) {
1537+
foundTypeInfo.emitNamedAsyncLetCapture(captureLoc,
1538+
targetDecl->getBaseIdentifier(),
1539+
targetDeclIsolationInfo);
1540+
return Action::Continue(expr);
1541+
}
1542+
}
1543+
1544+
return Action::Continue(expr);
1545+
}
1546+
};
1547+
14561548
bool TransferNonTransferrableDiagnosticInferrer::run() {
14571549
// We need to find the isolation info.
14581550
auto *op = diagnosticEmitter.getOperand();
@@ -1569,6 +1661,26 @@ bool TransferNonTransferrableDiagnosticInferrer::run() {
15691661
}
15701662
}
15711663

1664+
// If we are failing due to an autoclosure... see if we can find the captured
1665+
// value that is causing the issue.
1666+
if (auto *autoClosureExpr = loc.getAsASTNode<AutoClosureExpr>()) {
1667+
// To split up this work, we only do this for async let for now.
1668+
if (autoClosureExpr->getThunkKind() == AutoClosureExpr::Kind::AsyncLet) {
1669+
auto *i = op->getUser();
1670+
auto pai = ApplySite::isa(i);
1671+
unsigned captureIndex = pai.getAppliedArgIndex(*op);
1672+
auto captureInfo =
1673+
autoClosureExpr->getCaptureInfo().getCaptures()[captureIndex];
1674+
auto loc = RegularLocation(captureInfo.getLoc(), false /*implicit*/);
1675+
AutoClosureWalker walker(
1676+
diagnosticEmitter, captureInfo.getDecl(),
1677+
diagnosticEmitter.getIsolationRegionInfo().getIsolationInfo(), loc,
1678+
autoClosureExpr->getThunkKind() == AutoClosureExpr::Kind::AsyncLet);
1679+
autoClosureExpr->walk(walker);
1680+
return true;
1681+
}
1682+
}
1683+
15721684
diagnosticEmitter.emitUnknownUse(loc);
15731685
return true;
15741686
}

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -741,6 +741,13 @@ isIsolationInferenceBoundaryClosure(const AbstractClosureExpr *closure,
741741
}
742742
}
743743

744+
// An autoclosure for an async let acts as a boundary. It is non-Sendable
745+
// regardless of its context.
746+
if (auto *autoclosure = dyn_cast<AutoClosureExpr>(closure)) {
747+
if (autoclosure->getThunkKind() == AutoClosureExpr::Kind::AsyncLet)
748+
return true;
749+
}
750+
744751
return isSendableClosure(closure, forActorIsolation);
745752
}
746753

test/Concurrency/global_actor_function_types.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,12 @@ func stripActor(_ expr: @Sendable @autoclosure () -> (() -> ())) async {
327327
return await stripActor(mainActorFn) // expected-warning {{converting function value of type '@MainActor () -> ()' to '() -> ()' loses global actor 'MainActor'}}
328328
}
329329

330-
// NOTE: this warning is correct, but is only being caught by TypeCheckConcurrency's extra check.
330+
// We used to not emit an error here with strict-concurrency enabled since we
331+
// were inferring the async let to main actor isolated (which was incorrect). We
332+
// now always treat async let as non-isolated, so we get the same error in all
333+
// contexts.
331334
@MainActor func exampleWhereConstraintSolverHasWrongDeclContext_v2() async -> Int {
332-
async let a: () = noActor(mainActorFn) // expected-without-transferring-warning {{converting function value of type '@MainActor () -> ()' to '() -> ()' loses global actor 'MainActor'}}
335+
async let a: () = noActor(mainActorFn) // expected-warning {{converting function value of type '@MainActor () -> ()' to '() -> ()' loses global actor 'MainActor'}}
333336
await a
334337
}
335338

test/Concurrency/issue-57376.swift

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,47 +26,53 @@ func testAsyncSequence1Sendable<Seq: AsyncSequence>(_ seq: Seq) async throws whe
2626

2727
func testAsyncSequenceTypedPattern<Seq: AsyncSequence>(_ seq: Seq) async throws where Seq.Element == Int { // expected-targeted-and-complete-note {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{54-54=, Sendable}}
2828
// expected-no-transferring-tns-note @-1 {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{54-54=, Sendable}}
29-
async let result: Int = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{task or actor isolated value cannot be sent}}
30-
// expected-targeted-and-complete-warning @-1 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
31-
// expected-no-transferring-tns-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
29+
async let result: Int = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{sending 'seq' risks causing data races}}
30+
// expected-transferring-tns-note @-1 {{sending task-isolated 'seq' into async let risks causing data races between nonisolated and task-isolated uses}}
31+
// expected-targeted-and-complete-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
32+
// expected-no-transferring-tns-warning @-3 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
3233
let _ = try! await result
3334
}
3435

3536
func testAsyncSequenceTypedPattern1<Seq: AsyncSequence>(_ seq: Seq) async throws where Seq.Element == Int { // expected-targeted-and-complete-note {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{55-55=, Sendable}}
3637
// expected-no-transferring-tns-note @-1 {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{55-55=, Sendable}}
37-
async let _: Int = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{task or actor isolated value cannot be sent}}
38-
// expected-targeted-and-complete-warning @-1 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
39-
// expected-no-transferring-tns-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
38+
async let _: Int = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{sending 'seq' risks causing data races}}
39+
// expected-transferring-tns-note @-1 {{sending task-isolated 'seq' into async let risks causing data races between nonisolated and task-isolated uses}}
40+
// expected-targeted-and-complete-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
41+
// expected-no-transferring-tns-warning @-3 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
4042
}
4143

4244
func testAsyncSequence<Seq: AsyncSequence>(_ seq: Seq) async throws where Seq.Element == Int { // expected-targeted-and-complete-note {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{42-42=, Sendable}}
4345
// expected-no-transferring-tns-note @-1 {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{42-42=, Sendable}}
44-
async let result = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{task or actor isolated value cannot be sent}}
45-
// expected-targeted-and-complete-warning @-1 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
46-
// expected-no-transferring-tns-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
46+
async let result = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{sending 'seq' risks causing data races}}
47+
// expected-transferring-tns-note @-1 {{sending task-isolated 'seq' into async let risks causing data races between nonisolated and task-isolated uses}}
48+
// expected-targeted-and-complete-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
49+
// expected-no-transferring-tns-warning @-3 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
4750
let _ = try! await result
4851
}
4952

5053
func testAsyncSequence1<Seq: AsyncSequence>(_ seq: Seq) async throws where Seq.Element == Int { // expected-targeted-and-complete-note {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{43-43=, Sendable}}
5154
// expected-no-transferring-tns-note @-1 {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{43-43=, Sendable}}
52-
async let _ = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{task or actor isolated value cannot be sent}}
53-
// expected-targeted-and-complete-warning @-1 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
54-
// expected-no-transferring-tns-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
55+
async let _ = seq.reduce(0) { $0 + $1 } // expected-transferring-tns-warning {{sending 'seq' risks causing data races}}
56+
// expected-transferring-tns-note @-1 {{sending task-isolated 'seq' into async let risks causing data races between nonisolated and task-isolated uses}}
57+
// expected-targeted-and-complete-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
58+
// expected-no-transferring-tns-warning @-3 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
5559
}
5660

5761
func testAsyncSequence3<Seq>(_ seq: Seq) async throws where Seq: AsyncSequence, Seq.Element == Int { // expected-targeted-and-complete-note {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{28-28=: Sendable}}
5862
// expected-no-transferring-tns-note @-1 {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{28-28=: Sendable}}
59-
async let result = seq // expected-transferring-tns-warning {{task or actor isolated value cannot be sent}}
60-
// expected-targeted-and-complete-warning @-1 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
61-
// expected-no-transferring-tns-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
63+
async let result = seq // expected-transferring-tns-warning {{sending 'seq' risks causing data races}}
64+
// expected-transferring-tns-note @-1 {{sending task-isolated 'seq' into async let risks causing data races between nonisolated and task-isolated uses}}
65+
// expected-targeted-and-complete-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
66+
// expected-no-transferring-tns-warning @-3 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
6267
let _ = await result
6368
}
6469

6570
func testAsyncSequence4<Seq>(_ seq: Seq) async throws where Seq: AsyncSequence, Seq.Element == Int { // expected-targeted-and-complete-note {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{28-28=: Sendable}}
6671
// expected-no-transferring-tns-note @-1 {{consider making generic parameter 'Seq' conform to the 'Sendable' protocol}} {{28-28=: Sendable}}
67-
async let _ = seq // expected-transferring-tns-warning {{task or actor isolated value cannot be sent}}
68-
// expected-targeted-and-complete-warning @-1 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
69-
// expected-no-transferring-tns-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
72+
async let _ = seq // expected-transferring-tns-warning {{sending 'seq' risks causing data races}}
73+
// expected-transferring-tns-note @-1 {{sending task-isolated 'seq' into async let risks causing data races between nonisolated and task-isolated uses}}
74+
// expected-targeted-and-complete-warning @-2 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
75+
// expected-no-transferring-tns-warning @-3 {{capture of 'seq' with non-sendable type 'Seq' in 'async let' binding}}
7076
}
7177

7278
func search(query: String, entities: [String]) async throws -> [String] {

test/Concurrency/transfernonsendable_asynclet.swift

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ class NonSendableKlass {
1818

1919
class SendableKlass : @unchecked Sendable {}
2020

21-
actor Actor {
21+
struct NonSendableStruct { // expected-note {{}}
22+
var ns = NonSendableKlass()
23+
}
24+
25+
actor MyActor {
2226
var klass = NonSendableKlass()
2327
final var finalKlass = NonSendableKlass()
2428

@@ -28,7 +32,7 @@ actor Actor {
2832
func useNonSendableFunction(_: () -> Void) {}
2933
}
3034

31-
final actor FinalActor {
35+
final actor FinalMyActor {
3236
var klass = NonSendableKlass()
3337
func useKlass(_ x: NonSendableKlass) {}
3438
}
@@ -38,6 +42,13 @@ func useInOut<T>(_ x: inout T) {}
3842
func useValue<T>(_ x: T) -> T { x }
3943
func useValueWrapInOptional<T>(_ x: T) -> T? { x }
4044

45+
func useValueNoReturnWithInstance<T, V : Actor>(_ x: T, _ y: V) -> () { fatalError() }
46+
func useValueAsyncNoReturnWithInstance<T, V : Actor>(_ x: T, _ y: V) async -> () { fatalError() }
47+
@MainActor
48+
func useMainActorValueAsyncNoReturn<T>(_ x: T) async -> () { fatalError() }
49+
@MainActor
50+
func useMainActorValueNoReturn<T>(_ x: T) -> () { fatalError() }
51+
4152
@MainActor func returnValueFromMain<T>() async -> T { fatalError() }
4253
@MainActor func transferToMain<T>(_ t: T) async {}
4354
@MainActor func transferToMainInt<T>(_ t: T) async -> Int { 5 }
@@ -722,11 +733,50 @@ func asyncLetWithoutCapture() async {
722733
}
723734

724735
func asyncLet_Let_ActorIsolated_Method() async {
725-
let a = Actor()
736+
let a = MyActor()
726737
let x = NonSendableKlass()
727738
async let y = a.useKlass(x) // expected-warning {{sending 'x' risks causing data races}}
728739
// expected-note @-1 {{sending 'x' to actor-isolated instance method 'useKlass' risks causing data races between actor-isolated and local nonisolated uses}}
729740

730741
useValue(x) // expected-note {{access can happen concurrently}}
731742
let _ = await y
732743
}
744+
745+
extension NonSendableStruct {
746+
func asyncLetInferAsNonIsolated<T : Actor>(
747+
isolation actor: isolated T
748+
) async throws {
749+
async let subTask: Void = {
750+
await useValueAsyncNoReturnWithInstance(self, actor)
751+
// expected-warning @-1:47 {{sending 'self' risks causing data races}}
752+
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
753+
}()
754+
await subTask
755+
756+
async let subTask2: () = await useValueAsyncNoReturnWithInstance(self, actor)
757+
// expected-warning @-1 {{sending 'self' risks causing data races}}
758+
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
759+
await subTask2
760+
761+
async let subTask3: () = useValueNoReturnWithInstance(self, actor)
762+
// expected-warning @-1 {{sending 'self' risks causing data races}}
763+
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
764+
await subTask3
765+
766+
async let subTask4: () = await useMainActorValueAsyncNoReturn(self)
767+
// expected-warning @-1 {{sending 'self' risks causing data races}}
768+
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
769+
await subTask4
770+
771+
async let subTask5: () = useMainActorValueNoReturn(self)
772+
// expected-warning @-1 {{sending 'self' risks causing data races}}
773+
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
774+
await subTask5
775+
776+
async let subTask6: NonSendableStruct = self
777+
// expected-warning @-1 {{sending 'self' risks causing data races}}
778+
// expected-note @-2 {{sending 'actor'-isolated 'self' into async let risks causing data races between nonisolated and 'actor'-isolated uses}}
779+
// expected-warning @-3 {{non-sendable type 'NonSendableStruct' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary}}
780+
_ = await subTask6
781+
}
782+
}

0 commit comments

Comments
 (0)