Skip to content

Commit 03415be

Browse files
authored
Merge pull request #3948 from milseman/3_0_noescape_by_default
3.0 noescape by default
2 parents 6160971 + cc98458 commit 03415be

File tree

5 files changed

+125
-5
lines changed

5 files changed

+125
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2310,6 +2310,15 @@ ERROR(closure_noescape_use,none,
23102310
ERROR(decl_closure_noescape_use,none,
23112311
"declaration closing over non-escaping parameter %0 may allow it to escape",
23122312
(Identifier))
2313+
ERROR(passing_noescape_to_escaping,none,
2314+
"passing non-escaping parameter %0 to function expecting an @escaping closure",
2315+
(Identifier))
2316+
ERROR(assigning_noescape_to_escaping,none,
2317+
"assigning non-escaping parameter %0 to an @escaping closure",
2318+
(Identifier))
2319+
ERROR(general_noescape_to_escaping,none,
2320+
"using non-escaping parameter %0 in a context expecting an @escaping closure",
2321+
(Identifier))
23132322

23142323
ERROR(capture_across_type_decl,none,
23152324
"%0 declaration cannot close over value %1 defined in outer scope",

lib/Sema/CSDiag.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3593,6 +3593,62 @@ addTypeCoerceFixit(InFlightDiagnostic &diag, ConstraintSystem *CS,
35933593
return false;
35943594
}
35953595

3596+
/// Try to diagnose common errors involving implicitly non-escaping parameters
3597+
/// of function type, giving more specific and simpler diagnostics, attaching
3598+
/// notes on the parameter, and offering fixits to insert @escaping. Returns
3599+
/// true if it detects and issues an error, false if it does nothing.
3600+
static bool tryDiagnoseNonEscapingParameterToEscaping(Expr *expr, Type srcType,
3601+
Type dstType,
3602+
ConstraintSystem *CS) {
3603+
assert(expr && CS);
3604+
// Need to be referencing a parameter of function type
3605+
auto declRef = dyn_cast<DeclRefExpr>(expr);
3606+
if (!declRef || !isa<ParamDecl>(declRef->getDecl()) ||
3607+
!declRef->getType()->is<FunctionType>())
3608+
return false;
3609+
3610+
// Must be from non-escaping function to escaping function
3611+
auto srcFT = srcType->getAs<FunctionType>();
3612+
auto destFT = dstType->getAs<FunctionType>();
3613+
if (!srcFT || !destFT || !srcFT->isNoEscape() || destFT->isNoEscape())
3614+
return false;
3615+
3616+
// Function types must be equivalent modulo @escaping, @convention, etc.
3617+
if (!destFT->isEqual(srcFT->withExtInfo(destFT->getExtInfo())))
3618+
return false;
3619+
3620+
// Pick a specific diagnostic for the specific use
3621+
auto paramDecl = cast<ParamDecl>(declRef->getDecl());
3622+
switch (CS->getContextualTypePurpose()) {
3623+
case CTP_CallArgument:
3624+
CS->TC.diagnose(declRef->getLoc(), diag::passing_noescape_to_escaping,
3625+
paramDecl->getName());
3626+
break;
3627+
case CTP_AssignSource:
3628+
CS->TC.diagnose(declRef->getLoc(), diag::assigning_noescape_to_escaping,
3629+
paramDecl->getName());
3630+
break;
3631+
3632+
default:
3633+
CS->TC.diagnose(declRef->getLoc(), diag::general_noescape_to_escaping,
3634+
paramDecl->getName());
3635+
break;
3636+
}
3637+
3638+
// Give a note and fixit
3639+
InFlightDiagnostic note = CS->TC.diagnose(
3640+
paramDecl->getLoc(), srcFT->isAutoClosure() ? diag::noescape_autoclosure
3641+
: diag::noescape_parameter,
3642+
paramDecl->getName());
3643+
3644+
if (!srcFT->isAutoClosure()) {
3645+
note.fixItInsert(paramDecl->getTypeLoc().getSourceRange().Start,
3646+
"@escaping ");
3647+
} // TODO: add in a fixit for autoclosure
3648+
3649+
return true;
3650+
}
3651+
35963652
bool FailureDiagnosis::diagnoseContextualConversionError() {
35973653
// If the constraint system has a contextual type, then we can test to see if
35983654
// this is the problem that prevents us from solving the system.
@@ -3855,6 +3911,11 @@ bool FailureDiagnosis::diagnoseContextualConversionError() {
38553911
}
38563912
}
38573913

3914+
// Try for better/more specific diagnostics for non-escaping to @escaping
3915+
if (tryDiagnoseNonEscapingParameterToEscaping(expr, exprType, contextualType,
3916+
CS))
3917+
return true;
3918+
38583919
// When complaining about conversion to a protocol type, complain about
38593920
// conformance instead of "conversion".
38603921
if (contextualType->is<ProtocolType>() ||

test/attr/attr_autoclosure.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ func overloadedEach<P: P2>(_ source: P, _ closure: @escaping () -> ()) {
5555
struct S : P2 {
5656
typealias Element = Int
5757
func each(_ closure: @autoclosure () -> ()) {
58-
overloadedEach(self, closure) // expected-error {{invalid conversion from non-escaping function of type '@autoclosure () -> ()' to potentially escaping function type '() -> ()'}}
58+
// expected-note@-1{{parameter 'closure' is implicitly non-escaping because it was declared @autoclosure}}
59+
60+
overloadedEach(self, closure) // expected-error {{passing non-escaping parameter 'closure' to function expecting an @escaping closure}}
5961
}
6062
}
6163

@@ -87,7 +89,9 @@ class Sub : Super {
8789
func func12_sink(_ x: @escaping () -> Int) { }
8890

8991
func func12a(_ x: @autoclosure () -> Int) {
90-
func12_sink(x) // expected-error{{invalid conversion from non-escaping function of type '@autoclosure () -> Int' to potentially escaping function type '() -> Int'}}
92+
// expected-note@-1{{parameter 'x' is implicitly non-escaping because it was declared @autoclosure}}
93+
94+
func12_sink(x) // expected-error {{passing non-escaping parameter 'x' to function expecting an @escaping closure}}
9195
}
9296
func func12b(_ x: @autoclosure(escaping) () -> Int) {
9397
func12_sink(x)

test/attr/attr_escaping.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,48 @@ func wrongParamType(a: @escaping Int) {} // expected-error {{@escaping attribute
77
func conflictingAttrs(_ fn: @noescape @escaping () -> Int) {} // expected-error {{@escaping conflicts with @noescape}}
88

99
func takesEscaping(_ fn: @escaping () -> Int) {} // ok
10+
11+
func callEscapingWithNoEscape(_ fn: () -> Int) {
12+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{37-37=@escaping }}
13+
// expected-note@-2{{parameter 'fn' is implicitly non-escaping}} {{37-37=@escaping }}
14+
15+
takesEscaping(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
16+
let _ = fn // expected-error{{non-escaping parameter 'fn' may only be called}}
17+
}
18+
19+
typealias IntSugar = Int
20+
func callSugared(_ fn: () -> IntSugar) {
21+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{24-24=@escaping }}
22+
23+
takesEscaping(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
24+
}
25+
26+
struct StoresClosure {
27+
var closure : () -> Int
28+
init(_ fn: () -> Int) {
29+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{14-14=@escaping }}
30+
31+
closure = fn // expected-error{{assigning non-escaping parameter 'fn' to an @escaping closure}}
32+
}
33+
34+
func arrayPack(_ fn: () -> Int) -> [()->Int] {
35+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{24-24=@escaping }}
36+
37+
return [fn] // expected-error{{using non-escaping parameter 'fn' in a context expecting an @escaping closure}}
38+
}
39+
40+
func arrayPack(_ fn: @escaping () -> Int, _ fn2 : () -> Int) -> [()->Int] {
41+
// expected-note@-1{{parameter 'fn2' is implicitly non-escaping}} {{53-53=@escaping }}
42+
43+
return [fn, fn2] // expected-error{{using non-escaping parameter 'fn2' in a context expecting an @escaping closure}}
44+
}
45+
}
46+
47+
func takesEscapingBlock(_ fn: @escaping @convention(block) () -> Void) {
48+
fn()
49+
}
50+
func callEscapingWithNoEscapeBlock(_ fn: () -> Void) {
51+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{42-42=@escaping }}
52+
53+
takesEscapingBlock(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
54+
}

test/attr/attr_noescape.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ func takesNoEscapeClosure(_ fn : () -> Int) {
1313
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }}
1414
// expected-note@-2{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }}
1515
// expected-note@-3{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }}
16+
// expected-note@-4{{parameter 'fn' is implicitly non-escaping}} {{34-34=@escaping }}
1617
takesNoEscapeClosure { 4 } // ok
1718

1819
_ = fn() // ok
@@ -32,7 +33,7 @@ func takesNoEscapeClosure(_ fn : () -> Int) {
3233

3334
takesNoEscapeClosure(fn) // ok
3435

35-
doesEscape(fn) // expected-error {{invalid conversion from non-escaping function of type '() -> Int' to potentially escaping function type '() -> Int'}}
36+
doesEscape(fn) // expected-error {{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
3637
takesGenericClosure(4, fn) // ok
3738
takesGenericClosure(4) { fn() } // ok.
3839
}
@@ -254,8 +255,8 @@ func curriedFlatMap2<A, B>(_ x: [A]) -> (@noescape (A) -> [B]) -> [B] {
254255

255256
func bad(_ a : @escaping (Int)-> Int) -> Int { return 42 }
256257
func escapeNoEscapeResult(_ x: [Int]) -> (@noescape (Int) -> Int) -> Int {
257-
return { f in
258-
bad(f) // expected-error {{invalid conversion from non-escaping function of type '(Int) -> Int' to potentially escaping function type '(Int) -> Int'}}
258+
return { f in // expected-note{{parameter 'f' is implicitly non-escaping}}
259+
bad(f) // expected-error {{passing non-escaping parameter 'f' to function expecting an @escaping closure}}
259260
}
260261
}
261262

0 commit comments

Comments
 (0)