Skip to content

Commit 600fd17

Browse files
authored
Merge pull request #40434 from DougGregor/capture-sendable-checking
Diagnose non-sendable captures more directly.
2 parents 3f56ea8 + 9bd209d commit 600fd17

File tree

6 files changed

+67
-16
lines changed

6 files changed

+67
-16
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4491,6 +4491,10 @@ ERROR(concurrent_access_of_local_capture,none,
44914491
"%select{mutation of|reference to}0 captured %1 %2 in "
44924492
"concurrently-executing code",
44934493
(bool, DescriptiveDeclKind, DeclName))
4494+
ERROR(non_sendable_capture,none,
4495+
"capture of %1 with non-sendable type %0 in a `@Sendable` closure",
4496+
(Type, DeclName))
4497+
44944498
NOTE(actor_isolated_sync_func,none,
44954499
"calls to %0 %1 from outside of its actor context are "
44964500
"implicitly asynchronous",

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,29 @@ namespace {
12431243
return false;
12441244
}
12451245

1246+
/// Check closure captures for Sendable violations.
1247+
void checkClosureCaptures(AbstractClosureExpr *closure) {
1248+
if (!isSendableClosure(closure, /*forActorIsolation=*/false))
1249+
return;
1250+
1251+
SmallVector<CapturedValue, 2> captures;
1252+
closure->getCaptureInfo().getLocalCaptures(captures);
1253+
for (const auto &capture : captures) {
1254+
if (capture.isDynamicSelfMetadata())
1255+
continue;
1256+
if (capture.isOpaqueValue())
1257+
continue;
1258+
1259+
auto decl = capture.getDecl();
1260+
Type type = getDeclContext()
1261+
->mapTypeIntoContext(decl->getInterfaceType())
1262+
->getReferenceStorageReferent();
1263+
diagnoseNonSendableTypes(
1264+
type, getDeclContext(), capture.getLoc(),
1265+
diag::non_sendable_capture, decl->getName());
1266+
}
1267+
}
1268+
12461269
public:
12471270
ActorIsolationChecker(const DeclContext *dc) : ctx(dc->getASTContext()) {
12481271
contextStack.push_back(dc);
@@ -1315,6 +1338,7 @@ namespace {
13151338

13161339
if (auto *closure = dyn_cast<AbstractClosureExpr>(expr)) {
13171340
closure->setActorIsolation(determineClosureIsolation(closure));
1341+
checkClosureCaptures(closure);
13181342
contextStack.push_back(closure);
13191343
return { true, expr };
13201344
}
@@ -2233,9 +2257,7 @@ namespace {
22332257
if (!var->supportsMutation() ||
22342258
(ctx.LangOpts.EnableExperimentalFlowSensitiveConcurrentCaptures &&
22352259
parent.dyn_cast<LoadExpr *>())) {
2236-
return diagnoseNonSendableTypesInReference(
2237-
valueRef, getDeclContext(), loc,
2238-
ConcurrentReferenceKind::LocalCapture);
2260+
return false;
22392261
}
22402262

22412263
// Otherwise, we have concurrent access. Complain.

test/ClangImporter/objc_async.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,22 +128,22 @@ func testSendableAttrs(
128128

129129
doSomethingConcurrently {
130130
print(sendableClass) // no-error
131-
print(nonSendableClass) // expected-warning{{cannot use parameter 'nonSendableClass' with a non-sendable type 'NonSendableClass' from concurrently-executed code}}
131+
print(nonSendableClass) // expected-warning{{capture of 'nonSendableClass' with non-sendable type 'NonSendableClass' in a `@Sendable` closure}}
132132

133133
print(sendableEnum) // no-error
134-
print(nonSendableEnum) // expected-warning{{cannot use parameter 'nonSendableEnum' with a non-sendable type 'NonSendableEnum' from concurrently-executed code}}
134+
print(nonSendableEnum) // expected-warning{{capture of 'nonSendableEnum' with non-sendable type 'NonSendableEnum' in a `@Sendable` closure}}
135135

136136
print(sendableOptions) // no-error
137-
print(nonSendableOptions) // expected-warning{{cannot use parameter 'nonSendableOptions' with a non-sendable type 'NonSendableOptions' from concurrently-executed code}}
137+
print(nonSendableOptions) // expected-warning{{capture of 'nonSendableOptions' with non-sendable type 'NonSendableOptions' in a `@Sendable` closure}}
138138

139139
print(sendableError) // no-error
140140
print(nonSendableError) // no-error--we don't respect `@_nonSendable` on `ns_error_domain` types because all errors are Sendable
141141

142142
print(sendableStringEnum) // no-error
143-
print(nonSendableStringEnum) // expected-warning{{cannot use parameter 'nonSendableStringEnum' with a non-sendable type 'NonSendableStringEnum' from concurrently-executed code}}
143+
print(nonSendableStringEnum) // expected-warning{{capture of 'nonSendableStringEnum' with non-sendable type 'NonSendableStringEnum' in a `@Sendable` closure}}
144144

145145
print(sendableStringStruct) // no-error
146-
print(nonSendableStringStruct) // expected-warning{{cannot use parameter 'nonSendableStringStruct' with a non-sendable type 'NonSendableStringStruct' from concurrently-executed code}}
146+
print(nonSendableStringStruct) // expected-warning{{capture of 'nonSendableStringStruct' with non-sendable type 'NonSendableStringStruct' in a `@Sendable` closure}}
147147
}
148148
}
149149

test/Concurrency/actor_isolation.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,8 @@ func checkLocalFunctions() async {
617617

618618
// Escaping closures can make the local function execute concurrently.
619619
acceptConcurrentClosure {
620-
local2()
620+
local2() // expected-warning{{capture of 'local2()' with non-sendable type '() -> ()' in a `@Sendable` closure}}
621+
// expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
621622
}
622623

623624
print(i)
@@ -626,7 +627,8 @@ func checkLocalFunctions() async {
626627
var k = 17
627628
func local4() {
628629
acceptConcurrentClosure {
629-
local3()
630+
local3() // expected-warning{{capture of 'local3()' with non-sendable type '() -> ()' in a `@Sendable` closure}}
631+
// expected-note@-1{{a function type must be marked '@Sendable' to conform to 'Sendable'}}
630632
}
631633
}
632634

test/Concurrency/async_tasks.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ func test_nonsendableContinuation() async throws {
9999

100100
let _: NotSendable = try await withUnsafeThrowingContinuation { continuation in
101101
Task {
102-
continuation.resume(returning: NotSendable()) // expected-warning{{cannot use parameter 'continuation' with a non-sendable type 'UnsafeContinuation<NotSendable, Error>' from concurrently-executed code}}
102+
continuation.resume(returning: NotSendable()) // expected-warning{{capture of 'continuation' with non-sendable type 'UnsafeContinuation<NotSendable, Error>' in a `@Sendable` closure}}
103103
}
104104
}
105105
}

test/Concurrency/concurrent_value_checking.swift

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// RUN: %target-typecheck-verify-swift -disable-availability-checking -warn-concurrency
22
// REQUIRES: concurrency
33

4-
class NotConcurrent { } // expected-note 21{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
4+
class NotConcurrent { } // expected-note 26{{class 'NotConcurrent' does not conform to the 'Sendable' protocol}}
55

66
// ----------------------------------------------------------------------
77
// Sendable restriction on actor operations
@@ -131,8 +131,9 @@ func testConcurrency() {
131131
print(y) // okay
132132
}
133133
acceptConcurrent {
134-
print(x) // expected-warning{{cannot use let 'x' with a non-sendable type 'NotConcurrent' from concurrently-executed code}}
135-
print(y) // expected-error{{reference to captured var 'y' in concurrently-executing code}}
134+
print(x) // expected-warning{{capture of 'x' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
135+
print(y) // expected-warning{{capture of 'y' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
136+
// expected-error@-1{{reference to captured var 'y' in concurrently-executing code}}
136137
}
137138
}
138139

@@ -226,7 +227,7 @@ func acceptConcurrentUnary<T>(_: @Sendable (T) -> T) { }
226227
func concurrentClosures<T>(_: T) {
227228
acceptConcurrentUnary { (x: T) in
228229
_ = x // ok
229-
acceptConcurrentUnary { _ in x } // expected-warning{{cannot use parameter 'x' with a non-sendable type 'T' from concurrently-executed code}}
230+
acceptConcurrentUnary { _ in x } // expected-warning{{capture of 'x' with non-sendable type 'T' in a `@Sendable` closure}}
230231
}
231232
}
232233

@@ -286,6 +287,28 @@ final class C7<T>: Sendable { }
286287

287288
class C9: Sendable { } // expected-warning{{non-final class 'C9' cannot conform to 'Sendable'; use '@unchecked Sendable'}}
288289

290+
extension NotConcurrent {
291+
func f() { }
292+
293+
func test() {
294+
Task {
295+
f() // expected-warning{{capture of 'self' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
296+
}
297+
298+
Task {
299+
self.f() // expected-warning{{capture of 'self' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
300+
}
301+
302+
Task { [self] in
303+
f() // expected-warning{{capture of 'self' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
304+
}
305+
306+
Task { [self] in
307+
self.f() // expected-warning{{capture of 'self' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
308+
}
309+
}
310+
}
311+
289312
// ----------------------------------------------------------------------
290313
// @unchecked Sendable disabling checking
291314
// ----------------------------------------------------------------------
@@ -327,7 +350,7 @@ enum E12<T>: UnsafeSendable { // expected-warning{{'UnsafeSendable' is deprecate
327350
func testSendableOptionalInference(nc: NotConcurrent) {
328351
var fn: (@Sendable () -> Void)? = nil
329352
fn = {
330-
print(nc) // expected-warning{{cannot use parameter 'nc' with a non-sendable type 'NotConcurrent' from concurrently-executed code}}
353+
print(nc) // expected-warning{{capture of 'nc' with non-sendable type 'NotConcurrent' in a `@Sendable` closure}}
331354
}
332355
_ = fn
333356
}

0 commit comments

Comments
 (0)