Skip to content

Commit d6483ae

Browse files
authored
Merge pull request #68693 from hborla/non-sendable-local-captures
[Concurrency] Diagnose Sendable violations in captures of local functions.
2 parents 940ab81 + 2159c0c commit d6483ae

File tree

3 files changed

+30
-8
lines changed

3 files changed

+30
-8
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5237,8 +5237,9 @@ ERROR(concurrent_access_of_inout_param,none,
52375237
"concurrently-executing code",
52385238
(DeclName))
52395239
ERROR(non_sendable_capture,none,
5240-
"capture of %1 with non-sendable type %0 in a `@Sendable` closure",
5241-
(Type, DeclName))
5240+
"capture of %1 with non-sendable type %0 in a `@Sendable` "
5241+
"%select{local function|closure}2",
5242+
(Type, DeclName, bool))
52425243
ERROR(implicit_async_let_non_sendable_capture,none,
52435244
"capture of %1 with non-sendable type %0 in 'async let' binding",
52445245
(Type, DeclName))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,9 +2122,9 @@ namespace {
21222122
}
21232123

21242124
/// Check closure captures for Sendable violations.
2125-
void checkClosureCaptures(AbstractClosureExpr *closure) {
2125+
void checkLocalCaptures(AnyFunctionRef localFunc) {
21262126
SmallVector<CapturedValue, 2> captures;
2127-
closure->getCaptureInfo().getLocalCaptures(captures);
2127+
localFunc.getCaptureInfo().getLocalCaptures(captures);
21282128
for (const auto &capture : captures) {
21292129
if (capture.isDynamicSelfMetadata())
21302130
continue;
@@ -2134,14 +2134,19 @@ namespace {
21342134
// If the closure won't execute concurrently with the context in
21352135
// which the declaration occurred, it's okay.
21362136
auto decl = capture.getDecl();
2137-
if (!mayExecuteConcurrentlyWith(closure, decl->getDeclContext()))
2137+
auto *context = localFunc.getAsDeclContext();
2138+
if (!mayExecuteConcurrentlyWith(context, decl->getDeclContext()))
21382139
continue;
21392140

21402141
Type type = getDeclContext()
21412142
->mapTypeIntoContext(decl->getInterfaceType())
21422143
->getReferenceStorageReferent();
21432144

2144-
if (closure->isImplicit()) {
2145+
if (type->hasError())
2146+
continue;
2147+
2148+
auto *closure = localFunc.getAbstractClosureExpr();
2149+
if (closure && closure->isImplicit()) {
21452150
auto *patternBindingDecl = getTopPatternBindingDecl();
21462151
if (patternBindingDecl && patternBindingDecl->isAsyncLet()) {
21472152
diagnoseNonSendableTypes(
@@ -2156,7 +2161,8 @@ namespace {
21562161
}
21572162
} else {
21582163
diagnoseNonSendableTypes(type, getDeclContext(), capture.getLoc(),
2159-
diag::non_sendable_capture, decl->getName());
2164+
diag::non_sendable_capture, decl->getName(),
2165+
/*closure=*/closure != nullptr);
21602166
}
21612167
}
21622168
}
@@ -2224,6 +2230,10 @@ namespace {
22242230

22252231
PreWalkAction walkToDeclPre(Decl *decl) override {
22262232
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
2233+
if (func->isLocalContext()) {
2234+
checkLocalCaptures(func);
2235+
}
2236+
22272237
contextStack.push_back(func);
22282238
}
22292239

@@ -2258,7 +2268,7 @@ namespace {
22582268

22592269
if (auto *closure = dyn_cast<AbstractClosureExpr>(expr)) {
22602270
closure->setActorIsolation(determineClosureIsolation(closure));
2261-
checkClosureCaptures(closure);
2271+
checkLocalCaptures(closure);
22622272
contextStack.push_back(closure);
22632273
return Action::Continue(expr);
22642274
}

test/Concurrency/sendable_checking.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ final class NonSendable {
250250
// expected-note @-1 3 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
251251
// SendNonSendable emits 3 fewer errors here.
252252
// expected-targeted-and-complete-note @-3 5 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
253+
// expected-complete-and-sns-note @-4 {{class 'NonSendable' does not conform to the 'Sendable' protocol}}
253254
var value = ""
254255

255256
@MainActor
@@ -305,3 +306,13 @@ func callNonisolatedAsyncClosure(
305306
await f(ns)
306307
// expected-targeted-and-complete-warning@-1 {{passing argument of non-sendable type 'NonSendable' outside of main actor-isolated context may introduce data races}}
307308
}
309+
310+
@available(SwiftStdlib 5.1, *)
311+
func testLocalCaptures() {
312+
let ns = NonSendable()
313+
314+
@Sendable func a2() -> NonSendable {
315+
return ns
316+
// expected-complete-and-sns-warning@-1 {{capture of 'ns' with non-sendable type 'NonSendable' in a `@Sendable` local function}}
317+
}
318+
}

0 commit comments

Comments
 (0)