Skip to content

SIL: More descriptive diagnostic about escaping closure vs local function #27815

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions include/swift/AST/DiagnosticsSIL.def
Original file line number Diff line number Diff line change
Expand Up @@ -130,24 +130,33 @@ ERROR(capture_before_declaration_defer,none,
NOTE(captured_value_declared_here,none,
"captured value declared here", ())

#define SELECT_ESCAPING_CLOSURE_KIND "escaping %select{local function|closure}0"

// Invalid escaping capture diagnostics.
ERROR(escaping_inout_capture,none,
"escaping closure captures 'inout' parameter %0", (Identifier))
SELECT_ESCAPING_CLOSURE_KIND
" captures 'inout' parameter %1",
(unsigned, Identifier))
NOTE(inout_param_defined_here,none,
"parameter %0 is declared 'inout'", (Identifier))
ERROR(escaping_mutable_self_capture,none,
"escaping closure captures mutating 'self' parameter", ())
SELECT_ESCAPING_CLOSURE_KIND
" captures mutating 'self' parameter", (unsigned))

ERROR(escaping_noescape_param_capture,none,
"escaping closure captures non-escaping parameter %0", (Identifier))
SELECT_ESCAPING_CLOSURE_KIND
" captures non-escaping parameter %1", (unsigned, Identifier))
NOTE(noescape_param_defined_here,none,
"parameter %0 is implicitly non-escaping", (Identifier))

ERROR(escaping_noescape_var_capture,none,
"escaping closure captures non-escaping value", ())
SELECT_ESCAPING_CLOSURE_KIND
" captures non-escaping value", (unsigned))

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

#undef SELECT_ESCAPING_CLOSURE_KIND

NOTE(value_captured_transitively,none,
"captured indirectly by this call", ())

Expand Down
27 changes: 21 additions & 6 deletions lib/SILOptimizer/Mandatory/DiagnoseInvalidEscapingCaptures.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,19 +323,33 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC,
// Otherwise, we have at least one escaping use of a partial_apply
// capturing a non-escaping value. We need to emit diagnostics.

// Should match SELECT_ESCAPING_CLOSURE_KIND in DiagnosticsSIL.def.
enum {
EscapingLocalFunction,
EscapingClosure
} functionKind = EscapingClosure;

if (auto *F = PAI->getReferencedFunctionOrNull()) {
if (auto loc = F->getLocation()) {
if (loc.isASTNode<FuncDecl>())
functionKind = EscapingLocalFunction;
}
}
// First, diagnose the inout captures, if any.
for (auto inoutCapture : inoutCaptures) {
if (isUseOfSelfInInitializer(inoutCapture)) {
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture);
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture,
functionKind);
} else {
auto *param = getParamDeclFromOperand(inoutCapture->get());
if (param->isSelfParameter())
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture);
diagnose(Context, PAI->getLoc(), diag::escaping_mutable_self_capture,
functionKind);
else {
diagnose(Context, PAI->getLoc(), diag::escaping_inout_capture,
param->getName());
functionKind, param->getName());
diagnose(Context, param->getLoc(), diag::inout_param_defined_here,
param->getName());
param->getName());
}
}

Expand All @@ -346,11 +360,12 @@ static void checkPartialApply(ASTContext &Context, DeclContext *DC,
for (auto noEscapeCapture : noEscapeCaptures) {
if (auto *param = getParamDeclFromOperand(noEscapeCapture->get())) {
diagnose(Context, PAI->getLoc(), diag::escaping_noescape_param_capture,
param->getName());
functionKind, param->getName());
diagnose(Context, param->getLoc(), diag::noescape_param_defined_here,
param->getName());
} else {
diagnose(Context, PAI->getLoc(), diag::escaping_noescape_var_capture);
diagnose(Context, PAI->getLoc(), diag::escaping_noescape_var_capture,
functionKind);
}

diagnoseCaptureLoc(Context, DC, PAI, noEscapeCapture);
Expand Down
12 changes: 6 additions & 6 deletions test/SILOptimizer/invalid_escaping_captures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func badLocalFunctionCaptureInOut1(x: inout Int) { // expected-note {{parameter
x += 1 // expected-note {{captured here}}
}

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

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

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

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

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

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

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

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

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

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

// Make sure that withoutActuallyEscaping counts as a safe use.
Expand Down