Skip to content

Commit 220f4ce

Browse files
authored
Merge pull request swiftlang#75227 from hborla/6.0-self-capture-deinit-task
[6.0][Concurrency] Diagnose captures of `self` in a task created in `deinit`.
2 parents e10d961 + c34795e commit 220f4ce

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
@@ -5520,6 +5520,9 @@ ERROR(non_sendable_isolated_capture,none,
55205520
ERROR(implicit_async_let_non_sendable_capture,none,
55215521
"capture of %1 with non-sendable type %0 in 'async let' binding",
55225522
(Type, DeclName))
5523+
ERROR(self_capture_deinit_task,none,
5524+
"capture of 'self' in a closure that outlives deinit",
5525+
())
55235526
ERROR(implicit_non_sendable_capture,none,
55245527
"implicit capture of %1 requires that %0 conforms to `Sendable`",
55255528
(Type, DeclName))

lib/Sema/TypeCheckConcurrency.cpp

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

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

2820-
auto *closure = localFunc.getAbstractClosureExpr();
28212839
if (closure && closure->isImplicit()) {
28222840
auto *patternBindingDecl = getTopPatternBindingDecl();
28232841
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)