Skip to content

Commit 73ba98c

Browse files
authored
Merge pull request #75224 from hborla/self-capture-deinit-task
[Concurrency] Diagnose captures of `self` in a task created in `deinit`.
2 parents 8741ede + 18b747c commit 73ba98c

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5566,6 +5566,9 @@ ERROR(non_sendable_isolated_capture,none,
55665566
ERROR(implicit_async_let_non_sendable_capture,none,
55675567
"capture of %1 with non-sendable type %0 in 'async let' binding",
55685568
(Type, DeclName))
5569+
ERROR(self_capture_deinit_task,none,
5570+
"capture of 'self' in a closure that outlives deinit",
5571+
())
55695572
ERROR(implicit_non_sendable_capture,none,
55705573
"implicit capture of %1 requires that %0 conforms to `Sendable`",
55715574
(Type, DeclName))

lib/Sema/TypeCheckConcurrency.cpp

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2794,6 +2794,25 @@ namespace {
27942794
if (capture.isOpaqueValue())
27952795
continue;
27962796

2797+
auto *closure = localFunc.getAbstractClosureExpr();
2798+
2799+
// Diagnose a `self` capture inside an escaping `sending`
2800+
// `@Sendable` closure in a deinit, which almost certainly
2801+
// means `self` would escape deinit at runtime.
2802+
auto *explicitClosure = dyn_cast_or_null<ClosureExpr>(closure);
2803+
auto *dc = getDeclContext();
2804+
if (explicitClosure && isa<DestructorDecl>(dc) &&
2805+
!explicitClosure->getType()->isNoEscape() &&
2806+
(explicitClosure->isPassedToSendingParameter() ||
2807+
explicitClosure->isSendable())) {
2808+
auto var = dyn_cast_or_null<VarDecl>(capture.getDecl());
2809+
if (var && var->isSelfParameter()) {
2810+
ctx.Diags.diagnose(explicitClosure->getLoc(),
2811+
diag::self_capture_deinit_task)
2812+
.warnUntilSwiftVersion(6);
2813+
}
2814+
}
2815+
27972816
// If the closure won't execute concurrently with the context in
27982817
// which the declaration occurred, it's okay.
27992818
auto decl = capture.getDecl();
@@ -2818,7 +2837,6 @@ namespace {
28182837
if (type->hasError())
28192838
continue;
28202839

2821-
auto *closure = localFunc.getAbstractClosureExpr();
28222840
if (closure && closure->isImplicit()) {
28232841
auto *patternBindingDecl = getTopPatternBindingDecl();
28242842
if (patternBindingDecl && patternBindingDecl->isAsyncLet()) {
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// RUN: %target-typecheck-verify-swift -strict-concurrency=complete -disable-availability-checking
2+
3+
@MainActor
4+
class C {
5+
let x: Int = 0
6+
7+
deinit {
8+
// expected-warning@+1 {{capture of 'self' in a closure that outlives deinit; this is an error in the Swift 6 language mode}}
9+
Task { @MainActor in
10+
_ = self
11+
}
12+
13+
// expected-warning@+1 {{capture of 'self' in a closure that outlives deinit; this is an error in the Swift 6 language mode}}
14+
Task {
15+
_ = x
16+
}
17+
}
18+
}
19+
20+
func enqueueSomewhereElse(_ closure: @escaping @Sendable () -> Void) {}
21+
22+
@MainActor
23+
class C2 {
24+
let x: Int = 0
25+
26+
deinit {
27+
// expected-warning@+1 {{capture of 'self' in a closure that outlives deinit; this is an error in the Swift 6 language mode}}
28+
enqueueSomewhereElse {
29+
_ = self
30+
}
31+
32+
// expected-warning@+1 {{capture of 'self' in a closure that outlives deinit; this is an error in the Swift 6 language mode}}
33+
enqueueSomewhereElse {
34+
_ = self.x
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)