Skip to content

Commit 3913c31

Browse files
committed
[SR-11711] Single-expression returns for #if declarations within closures
1 parent 37b1733 commit 3913c31

File tree

4 files changed

+288
-28
lines changed

4 files changed

+288
-28
lines changed

lib/Parse/ParseExpr.cpp

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2839,39 +2839,60 @@ ParserResult<Expr> Parser::parseExprClosure() {
28392839
closure->setParameterList(params);
28402840
closure->setHasAnonymousClosureVars();
28412841
}
2842-
2842+
28432843
// If the body consists of a single expression, turn it into a return
28442844
// statement.
28452845
bool hasSingleExpressionBody = false;
2846-
if (!missingRBrace && bodyElements.size() == 1) {
2847-
// If the closure's only body element is a single return statement,
2848-
// use that instead of creating a new wrapping return expression.
2849-
Expr *returnExpr = nullptr;
2846+
if (!missingRBrace && bodyElements.size()) {
2847+
auto Element = bodyElements.front();
28502848

2851-
if (bodyElements[0].is<Stmt *>()) {
2852-
if (auto returnStmt =
2853-
dyn_cast<ReturnStmt>(bodyElements[0].get<Stmt*>())) {
2854-
2855-
if (!returnStmt->hasResult()) {
2856-
2857-
returnExpr = TupleExpr::createEmpty(Context,
2858-
SourceLoc(),
2859-
SourceLoc(),
2860-
/*implicit*/true);
2861-
2862-
returnStmt->setResult(returnExpr);
2849+
// If the body consists of an #if declaration with a single
2850+
// expression active clause, turn it into a return statement.
2851+
bool ReturnsLastElement = false;
2852+
if (bodyElements.size() == 2) {
2853+
if (auto *D = Element.dyn_cast<Decl *>()) {
2854+
// Step into nested active clause.
2855+
while (auto *ICD = dyn_cast<IfConfigDecl>(D)) {
2856+
auto ACE = ICD->getActiveClauseElements();
2857+
if (ACE.size() == 1) {
2858+
ReturnsLastElement = true;
2859+
Element = bodyElements.back();
2860+
break;
2861+
} else if (ACE.size() == 2) {
2862+
if (auto *ND = ACE.front().dyn_cast<Decl *>()) {
2863+
D = ND;
2864+
continue;
2865+
}
2866+
}
2867+
break;
28632868
}
2864-
2865-
hasSingleExpressionBody = true;
28662869
}
28672870
}
28682871

2869-
// Otherwise, create the wrapping return.
2870-
if (bodyElements[0].is<Expr *>()) {
2871-
hasSingleExpressionBody = true;
2872-
returnExpr = bodyElements[0].get<Expr*>();
2873-
bodyElements[0] = new (Context) ReturnStmt(SourceLoc(),
2874-
returnExpr);
2872+
if (ReturnsLastElement || bodyElements.size() == 1) {
2873+
// If the closure's only body element is a single return statement,
2874+
// use that instead of creating a new wrapping return expression.
2875+
Expr *returnExpr = nullptr;
2876+
2877+
if (Element.is<Stmt *>()) {
2878+
if (auto returnStmt = dyn_cast<ReturnStmt>(Element.get<Stmt *>())) {
2879+
if (!returnStmt->hasResult()) {
2880+
returnExpr = TupleExpr::createEmpty(Context,
2881+
SourceLoc(),
2882+
SourceLoc(),
2883+
/*implicit*/true);
2884+
returnStmt->setResult(returnExpr);
2885+
}
2886+
hasSingleExpressionBody = true;
2887+
}
2888+
}
2889+
2890+
// Otherwise, create the wrapping return.
2891+
if (Element.is<Expr *>()) {
2892+
hasSingleExpressionBody = true;
2893+
returnExpr = Element.get<Expr*>();
2894+
bodyElements.back() = new (Context) ReturnStmt(SourceLoc(), returnExpr);
2895+
}
28752896
}
28762897
}
28772898

lib/Sema/CSClosure.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,11 @@ class ClosureConstraintApplication
173173
}
174174

175175
void visitDecl(Decl *decl) {
176-
llvm_unreachable("Declarations not supported");
176+
177+
if (isa<IfConfigDecl>(decl))
178+
return;
179+
180+
llvm_unreachable("Unimplemented case for closure body");
177181
}
178182

179183
ASTNode visitBraceStmt(BraceStmt *braceStmt) {

test/FixCode/fixits-omit-return.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ let cl_fixit_addreturn: () -> String = {
1212

1313
func ff_fixit_addreturn_ifdecl() -> String {
1414
#if true
15-
print("entering ff_fixit_addreturn()")
15+
print("entering ff_fixit_addreturn_ifdecl()")
1616
"foo" // expected-warning {{string literal is unused}} expected-error {{missing return in a function expected to return 'String'; did you mean to return the last expression?}} {{5-5=return }}
1717
#endif
1818
}
1919

2020
let cl_fixit_addreturn_ifdecl: () -> String = {
2121
#if true
22-
print("entering cl_fixit_addreturn()")
22+
print("entering cl_fixit_addreturn_ifdecl()")
2323
"foo" // expected-warning {{string literal is unused}} expected-error {{missing return in a closure expected to return 'String'; did you mean to return the last expression?}} {{5-5=return }}
2424
#endif
2525
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
func takeIntToInt(_ f: (Int) -> Int) { }
4+
func takeIntIntToInt(_ f: (Int, Int) -> Int) { }
5+
6+
// Simple closures with anonymous arguments
7+
func simple() {
8+
takeIntToInt({
9+
#if true
10+
$0 + 1
11+
#else
12+
$0 + 2
13+
#endif
14+
})
15+
takeIntIntToInt({
16+
#if true
17+
$0 + $1 + 1
18+
#else
19+
$0 + $1 + 2
20+
#endif
21+
})
22+
}
23+
24+
// Anonymous arguments with inference
25+
func myMap<T, U>(_ array: [T], _ f: (T) -> U) -> [U] {}
26+
27+
func testMap(_ array: [Int]) {
28+
var farray = myMap(array, {
29+
#if true
30+
Float($0)
31+
#else
32+
Float($0 + 1)
33+
#endif
34+
})
35+
var _ : Float = farray[0]
36+
let farray2 = myMap(array, { (x : Int) in
37+
#if true
38+
Float(x)
39+
#else
40+
Float(x + 1)
41+
#endif
42+
})
43+
farray = farray2
44+
_ = farray
45+
}
46+
47+
// Nested single-expression closures -- <rdar://problem/20931915>
48+
class NestedSingleExpr {
49+
private var b: Bool = false
50+
private func callClosureA(_ callback: () -> Void) {}
51+
private func callClosureB(_ callback: () -> Void) {}
52+
53+
func call() {
54+
callClosureA { [weak self] in
55+
#if true
56+
self?.callClosureA {
57+
#if true
58+
self?.b = true
59+
#else
60+
self?.b = false
61+
#endif
62+
}
63+
#else
64+
self?.callClosureB {
65+
#if true
66+
self?.b = true
67+
#else
68+
self?.b = false
69+
#endif
70+
}
71+
#endif
72+
}
73+
}
74+
}
75+
76+
// Autoclosure nested inside single-expr closure should get discriminator
77+
// <rdar://problem/22441425> Swift compiler "INTERNAL ERROR: this diagnostic should not be produced"
78+
struct Expectation<T> {}
79+
func expect<T>(_ expression: @autoclosure () -> T) -> Expectation<T> {
80+
return Expectation<T>()
81+
}
82+
func describe(_ closure: () -> ()) {}
83+
func f() {
84+
#if true
85+
describe {
86+
#if false
87+
_ = expect("this")
88+
#else
89+
_ = expect("what")
90+
#endif
91+
}
92+
#endif
93+
}
94+
95+
struct Blob {}
96+
97+
func withBlob(block: (Blob) -> ()) {}
98+
99+
protocol Binding {}
100+
extension Int: Binding {}
101+
extension Double: Binding {}
102+
extension String: Binding {}
103+
extension Blob: Binding {}
104+
105+
struct Stmt {
106+
@discardableResult
107+
func bind(_ values: Binding?...) -> Stmt {
108+
return self
109+
}
110+
111+
@discardableResult
112+
func bind(_ values: [Binding?]) -> Stmt {
113+
return self
114+
}
115+
116+
@discardableResult
117+
func bind(_ values: [String: Binding?]) -> Stmt {
118+
return self
119+
}
120+
}
121+
122+
let stmt = Stmt()
123+
withBlob {
124+
#if true
125+
stmt.bind(1, 2.0, "3", $0)
126+
#endif
127+
}
128+
withBlob {
129+
#if true
130+
stmt.bind([1, 2.0, "3", $0])
131+
#endif
132+
}
133+
withBlob {
134+
#if true
135+
stmt.bind(["1": 1, "2": 2.0, "3": "3", "4": $0])
136+
#endif
137+
}
138+
139+
// <rdar://problem/19840785>
140+
// We shouldn't crash on the call to 'a.dispatch' below.
141+
class A {
142+
func dispatch(_ f : () -> Void) {
143+
f()
144+
}
145+
}
146+
147+
class C {
148+
var prop = 0
149+
var a = A()
150+
151+
func act() {
152+
a.dispatch({() -> Void in
153+
#if true
154+
self.prop // expected-warning {{expression of type 'Int' is unused}}
155+
#endif
156+
})
157+
}
158+
}
159+
160+
// Never-returning expressions
161+
func haltAndCatchFire() -> Never {
162+
#if true
163+
while true { }
164+
#else
165+
while false { }
166+
#endif
167+
}
168+
let backupPlan: () -> Int = {
169+
#if true
170+
haltAndCatchFire()
171+
#endif
172+
}
173+
func missionCritical(storage: () -> String) {}
174+
missionCritical(storage: {
175+
#if true
176+
haltAndCatchFire()
177+
#endif
178+
})
179+
180+
// <https://bugs.swift.org/browse/SR-4963>
181+
enum E { }
182+
func takesAnotherUninhabitedType(e: () -> E) {}
183+
takesAnotherUninhabitedType {
184+
#if true
185+
haltAndCatchFire()
186+
#endif
187+
}
188+
189+
// Weak capture bug caught by rdar://problem/67351438
190+
class Y {
191+
var toggle: Bool = false
192+
193+
func doSomething(animated: Bool, completionHandler: (Int, Int) -> Void) { }
194+
}
195+
196+
class X {
197+
private(set) var someY: Y!
198+
199+
func doSomething() {
200+
someY?.doSomething(animated: true, completionHandler: { [weak someY] _, _ in
201+
#if true
202+
someY?.toggle = true
203+
#else
204+
someY?.toggle = false
205+
#endif
206+
})
207+
}
208+
}
209+
210+
var intOrStringClosure_true = {
211+
#if true
212+
42
213+
#else
214+
"foo"
215+
#endif
216+
}
217+
218+
var intOrStringClosure_false = {
219+
#if false
220+
42
221+
#else
222+
"foo"
223+
#endif
224+
}
225+
226+
func testMultiType() {
227+
228+
let a = intOrStringClosure_true()
229+
_ = a as Int
230+
_ = a as String // expected-error {{cannot convert value of type 'Int' to type 'String'}}
231+
232+
let b = intOrStringClosure_false()
233+
_ = b as Int // expected-error {{cannot convert value of type 'String' to type 'Int'}}
234+
_ = b as String
235+
}

0 commit comments

Comments
 (0)