Skip to content

Commit 05898d8

Browse files
committed
SIL: More descriptive diagnostic about escaping closure vs local function
Fixes <rdar://problem/50554625>.
1 parent a1e59b6 commit 05898d8

File tree

3 files changed

+40
-16
lines changed

3 files changed

+40
-16
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,24 +130,33 @@ ERROR(capture_before_declaration_defer,none,
130130
NOTE(captured_value_declared_here,none,
131131
"captured value declared here", ())
132132

133+
#define SELECT_ESCAPING_CLOSURE_KIND "escaping %select{local function|closure}0"
134+
133135
// Invalid escaping capture diagnostics.
134136
ERROR(escaping_inout_capture,none,
135-
"escaping closure captures 'inout' parameter %0", (Identifier))
137+
SELECT_ESCAPING_CLOSURE_KIND
138+
" captures 'inout' parameter %1",
139+
(unsigned, Identifier))
136140
NOTE(inout_param_defined_here,none,
137141
"parameter %0 is declared 'inout'", (Identifier))
138142
ERROR(escaping_mutable_self_capture,none,
139-
"escaping closure captures mutating 'self' parameter", ())
143+
SELECT_ESCAPING_CLOSURE_KIND
144+
" captures mutating 'self' parameter", (unsigned))
140145

141146
ERROR(escaping_noescape_param_capture,none,
142-
"escaping closure captures non-escaping parameter %0", (Identifier))
147+
SELECT_ESCAPING_CLOSURE_KIND
148+
" captures non-escaping parameter %1", (unsigned, Identifier))
143149
NOTE(noescape_param_defined_here,none,
144150
"parameter %0 is implicitly non-escaping", (Identifier))
145151

146152
ERROR(escaping_noescape_var_capture,none,
147-
"escaping closure captures non-escaping value", ())
153+
SELECT_ESCAPING_CLOSURE_KIND
154+
" captures non-escaping value", (unsigned))
148155

149156
NOTE(value_captured_here,none,"captured here", ())
150157

158+
#undef SELECT_ESCAPING_CLOSURE_KIND
159+
151160
NOTE(value_captured_transitively,none,
152161
"captured indirectly by this call", ())
153162

lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -323,19 +323,33 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC,
323323
// Otherwise, we have at least one escaping use of a partial_apply
324324
// capturing a non-escaping value. We need to emit diagnostics.
325325

326+
// Should match SELECT_ESCAPING_CLOSURE_KIND in DiagnosticsSIL.def.
327+
enum {
328+
EscapingLocalFunction,
329+
EscapingClosure
330+
} functionKind = EscapingClosure;
331+
332+
if (auto *F = PAI->getReferencedFunctionOrNull()) {
333+
if (auto loc = F->getLocation()) {
334+
if (loc.isASTNode<FuncDecl>())
335+
functionKind = EscapingLocalFunction;
336+
}
337+
}
326338
// First, diagnose the inout captures, if any.
327339
for (auto inoutCapture : inoutCaptures) {
328340
if (isUseOfSelfInInitializer(inoutCapture)) {
329-
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture);
341+
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture,
342+
functionKind);
330343
} else {
331344
auto *param = getParamDeclFromOperand(inoutCapture->get());
332345
if (param->isSelfParameter())
333-
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture);
346+
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture,
347+
functionKind);
334348
else {
335349
diagnose(Context, PAI->getLoc(), diag::escaping_inout_capture,
336-
param->getName());
350+
functionKind, param->getName());
337351
diagnose(Context, param->getLoc(), diag::inout_param_defined_here,
338-
param->getName());
352+
param->getName());
339353
}
340354
}
341355

@@ -346,11 +360,12 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC,
346360
for (auto noEscapeCapture : noEscapeCaptures) {
347361
if (auto *param = getParamDeclFromOperand(noEscapeCapture->get())) {
348362
diagnose(Context, PAI->getLoc(), diag::escaping_noescape_param_capture,
349-
param->getName());
363+
functionKind, param->getName());
350364
diagnose(Context, param->getLoc(), diag::noescape_param_defined_here,
351365
param->getName());
352366
} else {
353-
diagnose(Context, PAI->getLoc(), diag::escaping_noescape_var_capture);
367+
diagnose(Context, PAI->getLoc(), diag::escaping_noescape_var_capture,
368+
functionKind);
354369
}
355370

356371
diagnoseCaptureLoc(Context, DC, PAI, noEscapeCapture);

test/SILOptimizer/invalid_escaping_captures.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func badLocalFunctionCaptureInOut1(x: inout Int) { // expected-note {{parameter
5353
x += 1 // expected-note {{captured here}}
5454
}
5555

56-
takesEscaping(local) // expected-error {{escaping closure captures 'inout' parameter 'x'}}
56+
takesEscaping(local) // expected-error {{escaping local function captures 'inout' parameter 'x'}}
5757
}
5858

5959
func badLocalFunctionCaptureInOut2(x: inout Int) { // expected-note {{parameter 'x' is declared 'inout'}}
@@ -75,15 +75,15 @@ func badLocalFunctionCaptureInOut3(x: inout Int) { // expected-note {{parameter
7575
local1() // expected-note {{captured indirectly by this call}}
7676
}
7777

78-
takesEscaping(local2) // expected-error {{escaping closure captures 'inout' parameter 'x'}}
78+
takesEscaping(local2) // expected-error {{escaping local function captures 'inout' parameter 'x'}}
7979
}
8080

8181
func badLocalFunctionCaptureNoEscape1(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
8282
func local() {
8383
y() // expected-note {{captured here}}
8484
}
8585

86-
takesEscaping(local) // expected-error {{escaping closure captures non-escaping parameter 'y'}}
86+
takesEscaping(local) // expected-error {{escaping local function captures non-escaping parameter 'y'}}
8787
}
8888

8989
func badLocalFunctionCaptureNoEscape2(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
@@ -105,7 +105,7 @@ func badLocalFunctionCaptureNoEscape3(y: () -> ()) { // expected-note {{paramete
105105
local1() // expected-note {{captured indirectly by this call}}
106106
}
107107

108-
takesEscaping(local2) // expected-error {{escaping closure captures non-escaping parameter 'y'}}
108+
takesEscaping(local2) // expected-error {{escaping local function captures non-escaping parameter 'y'}}
109109
}
110110

111111
func badLocalFunctionCaptureNoEscape4(y: () -> ()) { // expected-note {{parameter 'y' is implicitly non-escaping}}
@@ -117,7 +117,7 @@ func badLocalFunctionCaptureNoEscape4(y: () -> ()) { // expected-note {{paramete
117117
local1() // expected-note {{captured indirectly by this call}}
118118
}
119119

120-
takesEscaping(local2) // expected-error {{escaping closure captures non-escaping parameter 'y'}}
120+
takesEscaping(local2) // expected-error {{escaping local function captures non-escaping parameter 'y'}}
121121
}
122122

123123
// Capturing 'self' produces a different diagnostic.
@@ -151,7 +151,7 @@ func testGenericLocalFunctionReabstraction(x: inout Int) { // expected-note {{pa
151151
x += 1 // expected-note {{captured here}}
152152
return 0
153153
}
154-
takesEscapingGeneric(local) // expected-error {{escaping closure captures 'inout' parameter 'x'}}
154+
takesEscapingGeneric(local) // expected-error {{escaping local function captures 'inout' parameter 'x'}}
155155
}
156156

157157
// Make sure that withoutActuallyEscaping counts as a safe use.

0 commit comments

Comments
 (0)