Skip to content

Commit 9e9a1b9

Browse files
committed
[noescape by default] Add @autoclosure @escaping syntax
Adds the preferred syntax for escaping autoclosures, which is @autoclosure @escaping. Deprecates @autoclosure(escaping), and provides fixits.
1 parent b8c66b2 commit 9e9a1b9

File tree

11 files changed

+103
-33
lines changed

11 files changed

+103
-33
lines changed

include/swift/AST/Attr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class TypeAttributes {
5252
SourceLoc AtLoc;
5353
Optional<StringRef> convention = None;
5454

55+
/// For the deprecated @autoclosure(escaping) syntax
56+
bool isDeprecatedAutoclosureEscaping = false;
57+
5558
// For an opened existential type, the known ID.
5659
Optional<UUID> OpenedID;
5760

include/swift/AST/DiagnosticsParse.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,12 @@ ERROR(attr_noescape_conflicts_escaping_autoclosure,none,
12151215
ERROR(attr_noescape_implied_by_autoclosure,none,
12161216
"@noescape is implied by @autoclosure and should not be "
12171217
"redundantly specified", ())
1218+
WARNING(attr_autoclosure_escaping_deprecated,none,
1219+
"@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead",
1220+
())
1221+
WARNING(attr_noescape_deprecated,none,
1222+
"@noescape is the default and is deprecated; omit it",
1223+
())
12181224

12191225
// convention
12201226
ERROR(convention_attribute_expected_lparen,none,

lib/AST/ASTPrinter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3746,7 +3746,7 @@ class TypePrinter : public TypeVisitor<TypePrinter> {
37463746
if (info.isNoEscape())
37473747
Printer << "@autoclosure ";
37483748
else
3749-
Printer << "@autoclosure(escaping) ";
3749+
Printer << "@autoclosure @escaping ";
37503750
} else if (inParameterPrinting) {
37513751
if (!info.isNoEscape()) {
37523752
Printer << "@escaping ";

lib/AST/TypeRepr.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,13 +276,14 @@ void AttributedTypeRepr::printAttrs(llvm::raw_ostream &OS) const {
276276
void AttributedTypeRepr::printAttrs(ASTPrinter &Printer) const {
277277
const TypeAttributes &Attrs = getAttrs();
278278

279-
switch (Attrs.has(TAK_autoclosure)*2 + Attrs.has(TAK_noescape)) {
280-
case 0: break; // Nothing specified.
281-
case 1: Printer << "@noescape "; break;
282-
case 2: Printer << "@autoclosure(escaping) "; break;
283-
case 3: Printer << "@autoclosure "; break;
279+
if (Attrs.isDeprecatedAutoclosureEscaping) {
280+
// Deprecated:
281+
Printer << "@autoclosure(escaping) ";
282+
} else {
283+
if (Attrs.has(TAK_autoclosure)) Printer << "@autoclosure ";
284+
if (Attrs.has(TAK_escaping)) Printer << "@escaping ";
284285
}
285-
if (Attrs.has(TAK_escaping)) Printer << "@escaping ";
286+
286287
if (Attrs.has(TAK_thin)) Printer << "@thin ";
287288
if (Attrs.has(TAK_thick)) Printer << "@thick ";
288289
if (Attrs.convention.hasValue()) {

lib/Parse/ParseDecl.cpp

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
14441444
SourceLoc Loc = consumeToken();
14451445

14461446
bool isAutoclosureEscaping = false;
1447+
SourceLoc autoclosureEscapingRightParenLoc;
14471448
StringRef conventionName;
14481449

14491450
// Handle @autoclosure(escaping)
@@ -1463,7 +1464,7 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
14631464
if (isAutoclosureEscaping) {
14641465
consumeToken(tok::l_paren);
14651466
consumeToken(tok::identifier);
1466-
consumeToken(tok::r_paren);
1467+
autoclosureEscapingRightParenLoc = consumeToken(tok::r_paren);
14671468
}
14681469
} else if (attr == TAK_convention) {
14691470
SourceLoc LPLoc;
@@ -1511,22 +1512,30 @@ bool Parser::parseTypeAttribute(TypeAttributes &Attributes, bool justChecking) {
15111512
case TAK_autoclosure:
15121513
// Handle @autoclosure(escaping)
15131514
if (isAutoclosureEscaping) {
1515+
Attributes.isDeprecatedAutoclosureEscaping = true;
1516+
15141517
// @noescape @autoclosure(escaping) makes no sense.
1515-
if (Attributes.has(TAK_noescape))
1518+
if (Attributes.has(TAK_noescape)) {
15161519
diagnose(Loc, diag::attr_noescape_conflicts_escaping_autoclosure);
1517-
} else {
1518-
if (Attributes.has(TAK_noescape))
1519-
diagnose(Loc, diag::attr_noescape_implied_by_autoclosure);
1520-
Attributes.setAttr(TAK_noescape, Loc);
1520+
} else {
1521+
diagnose(Loc, diag::attr_autoclosure_escaping_deprecated)
1522+
.fixItReplace({Loc, autoclosureEscapingRightParenLoc},
1523+
"@autoclosure @escaping ");
1524+
}
1525+
} else if (Attributes.has(TAK_noescape)) {
1526+
diagnose(Loc, diag::attr_noescape_implied_by_autoclosure);
15211527
}
15221528
break;
15231529

15241530
case TAK_noescape:
15251531
// You can't specify @noescape and @autoclosure(escaping) together, and
15261532
// @noescape after @autoclosure is redundant.
1527-
if (Attributes.has(TAK_autoclosure)) {
1533+
if (Attributes.has(TAK_autoclosure) &&
1534+
Attributes.isDeprecatedAutoclosureEscaping) {
15281535
diagnose(Loc, diag::attr_noescape_conflicts_escaping_autoclosure);
15291536
return false;
1537+
} else if (Attributes.has(TAK_autoclosure)) {
1538+
diagnose(Loc, diag::attr_noescape_implied_by_autoclosure);
15301539
}
15311540
// You can't specify @noescape and @escaping together.
15321541
if (Attributes.has(TAK_escaping)) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1609,7 +1609,7 @@ void TypeChecker::checkAutoClosureAttr(ParamDecl *PD, AutoClosureAttr *attr) {
16091609
return;
16101610

16111611
// This decl attribute has been moved to being a type attribute.
1612-
auto text = attr->isEscaping() ? "@autoclosure(escaping) " : "@autoclosure ";
1612+
auto text = attr->isEscaping() ? "@autoclosure @escaping " : "@autoclosure ";
16131613
diagnose(attr->getLocation(), diag::attr_decl_attr_now_on_type,
16141614
"@autoclosure")
16151615
.fixItRemove(attr->getRangeWithAt())

lib/Sema/TypeCheckType.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,15 +1889,14 @@ Type TypeResolver::resolveAttributedType(TypeAttributes &attrs,
18891889
}
18901890

18911891
bool defaultNoEscape = false;
1892-
// TODO: Get rid of the need for checking autoclosure, by refactoring
1893-
// special autoclosure knowledge to just as "isEscaping" or similar.
1894-
if (isFunctionParam && !attrs.has(TAK_autoclosure)) {
1895-
// Closure params default to non-escaping
1896-
if (attrs.has(TAK_noescape)) {
1897-
// FIXME: diagnostic to tell user this is redundant and drop it
1898-
} else if (!attrs.has(TAK_escaping)) {
1899-
defaultNoEscape = isDefaultNoEscapeContext(DC);
1900-
}
1892+
if (isFunctionParam && !attrs.has(TAK_escaping) &&
1893+
!attrs.isDeprecatedAutoclosureEscaping) {
1894+
defaultNoEscape = isDefaultNoEscapeContext(DC);
1895+
}
1896+
1897+
if (isFunctionParam && attrs.has(TAK_noescape) &&
1898+
isDefaultNoEscapeContext(DC)) {
1899+
// FIXME: diagnostic to tell user this is redundant and drop it
19011900
}
19021901

19031902
// Resolve the function type directly with these attributes.

test/IDE/print_ast_tc_decls.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,8 +1293,8 @@ public func ParamAttrs1(a : @autoclosure () -> ()) {
12931293
a()
12941294
}
12951295

1296-
// PASS_PRINT_AST: public func ParamAttrs2(a: @autoclosure(escaping) () -> ())
1297-
public func ParamAttrs2(a : @autoclosure(escaping) () -> ()) {
1296+
// PASS_PRINT_AST: public func ParamAttrs2(a: @autoclosure @escaping () -> ())
1297+
public func ParamAttrs2(a : @autoclosure @escaping () -> ()) {
12981298
a()
12991299
}
13001300

test/SourceKit/CursorInfo/cursor_info.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ typealias MyAlias2<A, B> = MyAlias<A, B>
156156

157157
func paramAutoclosureNoescape1(@noescape _ msg: ()->String) {}
158158
func paramAutoclosureNoescape2(@autoclosure _ msg: ()->String) {}
159-
func paramAutoclosureNoescape3(@autoclosure(escaping) _ msg: ()->String) {}
159+
func paramAutoclosureNoescape3(@autoclosure @escaping _ msg: ()->String) {}
160160

161161
func paramDefaultPlaceholder(_ f: StaticString = #function, file: StaticString = #file, line: UInt = #line, col: UInt = #column, arr: [Int] = [], dict: [Int:Int] = [:], opt: Int? = nil, reg: Int = 1) {}
162162

test/attr/attr_autoclosure.swift

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,27 @@ struct AutoclosureEscapeTest {
7070
}
7171

7272
// @autoclosure(escaping)
73-
// expected-error @+1 {{@autoclosure is now an attribute on a parameter type, instead of on the parameter itself}} {{13-34=}} {{38-38=@autoclosure(escaping) }}
73+
// expected-error @+1 {{@autoclosure is now an attribute on a parameter type, instead of on the parameter itself}} {{13-34=}} {{38-38=@autoclosure @escaping }}
7474
func func10(@autoclosure(escaping _: () -> ()) { } // expected-error{{expected ')' in @autoclosure}}
7575
// expected-note@-1{{to match this opening '('}}
7676
7777
func func11(_: @autoclosure(escaping) @noescape () -> ()) { } // expected-error{{@noescape conflicts with @autoclosure(escaping)}}
78-
78+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{17-38=@autoclosure @escaping }}
7979

8080
class Super {
8181
func f1(_ x: @autoclosure(escaping) () -> ()) { }
82+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{17-38=@autoclosure @escaping }}
8283
func f2(_ x: @autoclosure(escaping) () -> ()) { } // expected-note {{potential overridden instance method 'f2' here}}
84+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{17-38=@autoclosure @escaping }}
8385
func f3(x: @autoclosure () -> ()) { }
8486
}
8587

8688
class Sub : Super {
8789
override func f1(_ x: @autoclosure(escaping)() -> ()) { }
90+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{26-47=@autoclosure @escaping }}
8891
override func f2(_ x: @autoclosure () -> ()) { } // expected-error{{does not override any method}}
8992
override func f3(_ x: @autoclosure(escaping) () -> ()) { } // expected-error{{does not override any method}}
93+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{26-47=@autoclosure @escaping }}
9094
}
9195

9296
func func12_sink(_ x: @escaping () -> Int) { }
@@ -97,7 +101,14 @@ func func12a(_ x: @autoclosure () -> Int) {
97101
func12_sink(x) // expected-error {{passing non-escaping parameter 'x' to function expecting an @escaping closure}}
98102
}
99103
func func12b(_ x: @autoclosure(escaping) () -> Int) {
100-
func12_sink(x)
104+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{20-41=@autoclosure @escaping }}
105+
func12_sink(x) // ok
106+
}
107+
func func12c(_ x: @autoclosure @escaping () -> Int) {
108+
func12_sink(x) // ok
109+
}
110+
func func12d(_ x: @escaping @autoclosure () -> Int) {
111+
func12_sink(x) // ok
101112
}
102113

103114
class TestFunc12 {
@@ -121,15 +132,27 @@ enum AutoclosureFailableOf<T> {
121132

122133
let _ : (@autoclosure () -> ()) -> ()
123134
let _ : (@autoclosure(escaping) () -> ()) -> ()
135+
// expected-warning@-1{{@autoclosure(escaping) is deprecated; use @autoclosure @escaping instead}} {{11-32=@autoclosure @escaping }}
124136

125137
// escaping is the name of param type
126138
let _ : (@autoclosure(escaping) -> ()) -> () // expected-error {{use of undeclared type 'escaping'}}
127139

128-
129140
// Migration
130141
// expected-error @+1 {{@autoclosure is now an attribute on a parameter type, instead of on the parameter itself}} {{16-28=}} {{32-32=@autoclosure }}
131142
func migrateAC(@autoclosure _: () -> ()) { }
132143

133-
// expected-error @+1 {{@autoclosure is now an attribute on a parameter type, instead of on the parameter itself}} {{17-39=}} {{43-43=@autoclosure(escaping) }}
144+
// expected-error @+1 {{@autoclosure is now an attribute on a parameter type, instead of on the parameter itself}} {{17-39=}} {{43-43=@autoclosure @escaping }}
134145
func migrateACE(@autoclosure(escaping) _: () -> ()) { }
135146

147+
func takesAutoclosure(_ fn: @autoclosure () -> Int) {}
148+
149+
func callAutoclosureWithNoEscape(_ fn: () -> Int) {
150+
takesAutoclosure(1+1) // ok
151+
}
152+
func callAutoclosureWithNoEscape_2(_ fn: () -> Int) {
153+
takesAutoclosure(fn()) // ok
154+
}
155+
func callAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
156+
takesAutoclosure(fn()) // ok
157+
}
158+

test/attr/attr_escaping.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,33 @@ func callEscapingWithNoEscapeBlock(_ fn: () -> Void) {
5151
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}} {{42-42=@escaping }}
5252

5353
takesEscapingBlock(fn) // expected-error{{passing non-escaping parameter 'fn' to function expecting an @escaping closure}}
54-
}
54+
}
55+
56+
func takesEscapingAutoclosure(_ fn: @autoclosure @escaping () -> Int) {}
57+
58+
func callEscapingAutoclosureWithNoEscape(_ fn: () -> Int) {
59+
takesEscapingAutoclosure(1+1)
60+
}
61+
func callEscapingAutoclosureWithNoEscape_2(_ fn: () -> Int) {
62+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}}
63+
64+
takesEscapingAutoclosure(fn()) // expected-error{{closure use of non-escaping parameter 'fn' may allow it to escape}}
65+
}
66+
func callEscapingAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
67+
// expected-note@-1{{parameter 'fn' is implicitly non-escaping}}
68+
69+
takesEscapingAutoclosure(fn()) // expected-error{{closure use of non-escaping parameter 'fn' may allow it to escape}}
70+
}
71+
72+
func takesAutoclosure(_ fn: @autoclosure () -> Int) {}
73+
74+
func callAutoclosureWithNoEscape(_ fn: () -> Int) {
75+
takesAutoclosure(1+1) // ok
76+
}
77+
func callAutoclosureWithNoEscape_2(_ fn: () -> Int) {
78+
takesAutoclosure(fn()) // ok
79+
}
80+
func callAutoclosureWithNoEscape_3(_ fn: @autoclosure () -> Int) {
81+
takesAutoclosure(fn()) // ok
82+
}
83+

0 commit comments

Comments
 (0)