Skip to content

Commit 1c2bc06

Browse files
Merge pull request #31139 from ravikandhadai/autoclosure-capture-diagnostics
[SIL Diagnostics] Improve diagnostics for capture of inout values in escaping autoclosures.
2 parents c11f013 + a2d38f6 commit 1c2bc06

File tree

4 files changed

+85
-8
lines changed

4 files changed

+85
-8
lines changed

include/swift/AST/DiagnosticsSIL.def

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ ERROR(capture_before_declaration_defer,none,
108108
NOTE(captured_value_declared_here,none,
109109
"captured value declared here", ())
110110

111-
#define SELECT_ESCAPING_CLOSURE_KIND "escaping %select{local function|closure}0"
111+
#define SELECT_ESCAPING_CLOSURE_KIND "escaping %select{local function|closure|autoclosure}0"
112112

113113
// Invalid escaping capture diagnostics.
114114
ERROR(escaping_inout_capture,none,
@@ -133,6 +133,10 @@ ERROR(escaping_noescape_var_capture,none,
133133

134134
NOTE(value_captured_here,none,"captured here", ())
135135

136+
NOTE(copy_inout_captured_by_autoclosure,none, "pass a copy of %0", (Identifier))
137+
138+
NOTE(copy_self_captured_by_autoclosure,none, "pass a copy of 'self'", ())
139+
136140
#undef SELECT_ESCAPING_CLOSURE_KIND
137141

138142
NOTE(value_captured_transitively,none,

lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "swift/AST/ASTContext.h"
1919
#include "swift/AST/DiagnosticsSIL.h"
20+
#include "swift/AST/Expr.h"
2021
#include "swift/AST/Types.h"
2122
#include "swift/SIL/ApplySite.h"
2223
#include "swift/SIL/InstructionUtils.h"
@@ -332,17 +333,22 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC,
332333
// Should match SELECT_ESCAPING_CLOSURE_KIND in DiagnosticsSIL.def.
333334
enum {
334335
EscapingLocalFunction,
335-
EscapingClosure
336+
EscapingClosure,
337+
EscapingAutoClosure,
336338
} functionKind = EscapingClosure;
337339

338340
if (auto *F = PAI->getReferencedFunctionOrNull()) {
339341
if (auto loc = F->getLocation()) {
340-
if (loc.isASTNode<FuncDecl>())
342+
if (loc.isASTNode<FuncDecl>()) {
341343
functionKind = EscapingLocalFunction;
344+
} else if (loc.isASTNode<AutoClosureExpr>()) {
345+
functionKind = EscapingAutoClosure;
346+
}
342347
}
343348
}
344349
// First, diagnose the inout captures, if any.
345350
for (auto inoutCapture : inoutCaptures) {
351+
Optional<Identifier> paramName = None;
346352
if (isUseOfSelfInInitializer(inoutCapture)) {
347353
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture,
348354
functionKind);
@@ -352,14 +358,23 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC,
352358
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture,
353359
functionKind);
354360
else {
361+
paramName = param->getName();
355362
diagnose(Context, PAI->getLoc(), diag::escaping_inout_capture,
356363
functionKind, param->getName());
357364
diagnose(Context, param->getLoc(), diag::inout_param_defined_here,
358365
param->getName());
359366
}
360367
}
361-
362-
diagnoseCaptureLoc(Context, DC, PAI, inoutCapture);
368+
if (functionKind != EscapingAutoClosure) {
369+
diagnoseCaptureLoc(Context, DC, PAI, inoutCapture);
370+
continue;
371+
}
372+
// For an autoclosure capture, present a way to fix the problem.
373+
if (paramName)
374+
diagnose(Context, PAI->getLoc(), diag::copy_inout_captured_by_autoclosure,
375+
paramName.getValue());
376+
else
377+
diagnose(Context, PAI->getLoc(), diag::copy_self_captured_by_autoclosure);
363378
}
364379

365380
// Finally, diagnose captures of values with noescape type.

test/SILOptimizer/OSLogCompilerDiagnosticsTest.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ func testUnreachableLogCall(c: Color) {
5858

5959
// Passing InOut values to the logger should not crash the compiler.
6060
func foo(_ mutableValue: inout String) {
61+
// expected-note@-1 {{parameter 'mutableValue' is declared 'inout'}}
6162
_osLogTestHelper("FMFLabelledLocation: initialized with coder \(mutableValue)")
62-
// expected-error@-1 {{escaping closure captures 'inout' parameter 'mutableValue'}}
63-
// expected-note@-3 {{parameter 'mutableValue' is declared 'inout'}}
64-
// expected-note@-3 {{captured here}}
63+
// expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'mutableValue'}}
64+
// expected-note@-2 {{pass a copy of 'mutableValue'}}
6565
}
6666

6767
// This is an extension used only for testing a diagnostic that doesn't arise

test/SILOptimizer/invalid_escaping_captures.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,61 @@ public struct SelfEscapeFromInit {
189189

190190
public mutating func handler() {}
191191
}
192+
193+
func autoclosureTakesEscaping(_ x: @escaping @autoclosure () ->Int) {}
194+
195+
// Test that captures of escaping autoclosure are diagnosed correctly.
196+
func badCaptureInAutoclosure(x: inout Int) {
197+
// expected-note@-1 {{parameter 'x' is declared 'inout'}}
198+
// expected-note@-2 {{parameter 'x' is declared 'inout'}}
199+
200+
autoclosureTakesEscaping(x)
201+
// expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'x'}}
202+
// expected-note@-2 {{pass a copy of 'x'}}
203+
204+
autoclosureTakesEscaping((x + 1) - 100)
205+
// expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'x'}}
206+
// expected-note@-2 {{pass a copy of 'x'}}
207+
}
208+
209+
// Test that transitive captures in autoclosures are diagnosed correctly.
210+
func badTransitiveCaptureInClosures(x: inout Int) -> ((Int) -> Void) {
211+
// expected-note@-1 {{parameter 'x' is declared 'inout'}}
212+
// expected-note@-2 {{parameter 'x' is declared 'inout'}}
213+
// expected-note@-3 {{parameter 'x' is declared 'inout'}}
214+
215+
// Test capture of x by an autoclosure within a non-escaping closure.
216+
let _ = { (y: Int) in
217+
autoclosureTakesEscaping(x + y)
218+
// expected-error@-1 {{escaping autoclosure captures 'inout' parameter 'x'}}
219+
// expected-note@-2 {{pass a copy of 'x'}}
220+
}
221+
222+
// Test capture of x by an autoclosure within an escaping closure.
223+
let escapingClosure = { (y: Int) in
224+
// expected-error@-1 {{escaping closure captures 'inout' parameter 'x'}}
225+
226+
autoclosureTakesEscaping(x + y)
227+
// expected-note@-1 {{captured indirectly by this call}}
228+
// expected-note@-2 {{captured here}}
229+
230+
// expected-error@-4 {{escaping autoclosure captures 'inout' parameter 'x'}}
231+
// expected-note@-5 {{pass a copy of 'x'}}
232+
}
233+
return escapingClosure
234+
}
235+
236+
// Test that captures of mutating 'self' in escaping autoclosures are diagnosed correctly.
237+
struct S {
238+
var i = 0
239+
init() {
240+
autoclosureTakesEscaping(i)
241+
// expected-error@-1 {{escaping autoclosure captures mutating 'self' parameter}}
242+
// expected-note@-2 {{pass a copy of 'self'}}
243+
}
244+
mutating func method() {
245+
autoclosureTakesEscaping(i)
246+
// expected-error@-1 {{escaping autoclosure captures mutating 'self' parameter}}
247+
// expected-note@-2 {{pass a copy of 'self'}}
248+
}
249+
}

0 commit comments

Comments
 (0)