Skip to content

Commit 9a22f72

Browse files
committed
Handle #if for if/switch expressions
- Allow an if/switch expression to become an implicit return of a function that has a `#if` body with a single active element that is an `if` or `switch`. - Allow `#if` branches of an if/switch expression, as long as there is a single active expression element. rdar://107487977
1 parent 590c773 commit 9a22f72

File tree

11 files changed

+805
-45
lines changed

11 files changed

+805
-45
lines changed

include/swift/AST/Stmt.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,15 @@ class BraceStmt final : public Stmt,
217217
/// Otherwise returns \c nullptr.
218218
ASTNode getSingleActiveElement() const;
219219

220-
/// If this brace is wrapping a single expression, returns it. Otherwise
221-
/// returns \c nullptr.
222-
Expr *getSingleExpressionElement() const;
220+
/// If this brace is wrapping a single active expression, returns it. This
221+
/// includes both a single expression element, or a single expression in an
222+
/// active \c #if. Otherwise returns \c nullptr.
223+
Expr *getSingleActiveExpression() const;
224+
225+
/// If this brace is wrapping a single active statement, returns it. This
226+
/// includes both a single statement element, or a single statement in an
227+
/// active \c #if. Otherwise returns \c nullptr.
228+
Stmt *getSingleActiveStatement() const;
223229

224230
static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Brace; }
225231
};

lib/AST/Expr.cpp

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,11 +2524,7 @@ SingleValueStmtExpr *SingleValueStmtExpr::createWithWrappedBranches(
25242524
if (!BS)
25252525
continue;
25262526

2527-
auto elts = BS->getElements();
2528-
if (elts.size() != 1)
2529-
continue;
2530-
2531-
auto *S = elts.front().dyn_cast<Stmt *>();
2527+
auto *S = BS->getSingleActiveStatement();
25322528
if (!S)
25332529
continue;
25342530

@@ -2615,7 +2611,7 @@ ArrayRef<Expr *> SingleValueStmtExpr::getSingleExprBranches(
26152611
auto *BS = dyn_cast<BraceStmt>(branch);
26162612
if (!BS)
26172613
continue;
2618-
if (auto *E = BS->getSingleExpressionElement())
2614+
if (auto *E = BS->getSingleActiveExpression())
26192615
scratch.push_back(E);
26202616
}
26212617
return scratch;

lib/AST/Stmt.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -324,11 +324,12 @@ ASTNode BraceStmt::getSingleActiveElement() const {
324324
return hasSingleActiveElement(getElements()) ? getLastElement() : nullptr;
325325
}
326326

327-
Expr *BraceStmt::getSingleExpressionElement() const {
328-
if (getElements().size() != 1)
329-
return nullptr;
327+
Expr *BraceStmt::getSingleActiveExpression() const {
328+
return getSingleActiveElement().dyn_cast<Expr *>();
329+
}
330330

331-
return getElements()[0].dyn_cast<Expr *>();
331+
Stmt *BraceStmt::getSingleActiveStatement() const {
332+
return getSingleActiveElement().dyn_cast<Stmt *>();
332333
}
333334

334335
IsSingleValueStmtResult Stmt::mayProduceSingleValue(Evaluator &eval) const {

lib/Sema/PreCheckExpr.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,15 +1354,13 @@ bool PreCheckExpression::walkToClosureExprPre(ClosureExpr *closure) {
13541354
// LeaveClosureBodiesUnchecked, as the closure may become a single expression
13551355
// closure.
13561356
auto *body = closure->getBody();
1357-
if (body->getNumElements() == 1) {
1358-
if (auto *S = body->getLastElement().dyn_cast<Stmt *>()) {
1359-
if (S->mayProduceSingleValue(Ctx)) {
1360-
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
1361-
Ctx, S, /*DC*/ closure, /*mustBeExpr*/ false);
1362-
auto *RS = new (Ctx) ReturnStmt(SourceLoc(), SVE);
1363-
body->setLastElement(RS);
1364-
closure->setBody(body, /*isSingleExpression*/ true);
1365-
}
1357+
if (auto *S = body->getSingleActiveStatement()) {
1358+
if (S->mayProduceSingleValue(Ctx)) {
1359+
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
1360+
Ctx, S, /*DC*/ closure, /*mustBeExpr*/ false);
1361+
auto *RS = new (Ctx) ReturnStmt(SourceLoc(), SVE);
1362+
body->setLastElement(RS);
1363+
closure->setBody(body, /*isSingleExpression*/ true);
13661364
}
13671365
}
13681366

lib/Sema/TypeCheckStmt.cpp

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2658,23 +2658,26 @@ TypeCheckFunctionBodyRequest::evaluate(Evaluator &evaluator,
26582658

26592659
body->walk(ContextualizeClosuresAndMacros(AFD));
26602660
}
2661-
} else if (func->hasSingleExpressionBody() &&
2662-
func->getResultInterfaceType()->isVoid()) {
2663-
// The function returns void. We don't need an explicit return, no matter
2664-
// what the type of the expression is. Take the inserted return back out.
2665-
body->setLastElement(func->getSingleExpressionBody());
2666-
} else if (func->getBody()->getNumElements() == 1 &&
2667-
!func->getResultInterfaceType()->isVoid()) {
2661+
} else {
2662+
if (func->hasSingleExpressionBody() &&
2663+
func->getResultInterfaceType()->isVoid()) {
2664+
// The function returns void. We don't need an explicit return, no
2665+
// matter what the type of the expression is. Take the inserted return
2666+
// back out.
2667+
body->setLastElement(func->getSingleExpressionBody());
2668+
}
26682669
// If there is a single statement in the body that can be turned into a
26692670
// single expression return, do so now.
2670-
if (auto *S = func->getBody()->getLastElement().dyn_cast<Stmt *>()) {
2671-
if (S->mayProduceSingleValue(evaluator)) {
2672-
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
2673-
ctx, S, /*DC*/ func, /*mustBeExpr*/ false);
2674-
auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE);
2675-
body->setLastElement(RS);
2676-
func->setHasSingleExpressionBody();
2677-
func->setSingleExpressionBody(SVE);
2671+
if (!func->getResultInterfaceType()->isVoid()) {
2672+
if (auto *S = body->getSingleActiveStatement()) {
2673+
if (S->mayProduceSingleValue(evaluator)) {
2674+
auto *SVE = SingleValueStmtExpr::createWithWrappedBranches(
2675+
ctx, S, /*DC*/ func, /*mustBeExpr*/ false);
2676+
auto *RS = new (ctx) ReturnStmt(SourceLoc(), SVE);
2677+
body->setLastElement(RS);
2678+
func->setHasSingleExpressionBody();
2679+
func->setSingleExpressionBody(SVE);
2680+
}
26782681
}
26792682
}
26802683
}
@@ -2872,20 +2875,17 @@ areBranchesValidForSingleValueStmt(Evaluator &eval, ArrayRef<Stmt *> branches) {
28722875
// Check to see if there are any invalid jumps.
28732876
BS->walk(jumpFinder);
28742877

2875-
if (BS->getSingleExpressionElement()) {
2878+
if (BS->getSingleActiveExpression()) {
28762879
hadSingleExpr = true;
28772880
continue;
28782881
}
28792882

28802883
// We also allow single value statement branches, which we can wrap in
28812884
// a SingleValueStmtExpr.
2882-
auto elts = BS->getElements();
2883-
if (elts.size() == 1) {
2884-
if (auto *S = elts.back().dyn_cast<Stmt *>()) {
2885-
if (S->mayProduceSingleValue(eval)) {
2886-
hadSingleExpr = true;
2887-
continue;
2888-
}
2885+
if (auto *S = BS->getSingleActiveStatement()) {
2886+
if (S->mayProduceSingleValue(eval)) {
2887+
hadSingleExpr = true;
2888+
continue;
28892889
}
28902890
}
28912891
if (!doesBraceEndWithThrow(BS))

test/Constraints/if_expr.swift

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,100 @@ func testThrowInference() {
463463
}
464464
}
465465

466+
// MARK: Pound if
467+
468+
func testPoundIf1() -> Int {
469+
if .random() {
470+
#if true
471+
0
472+
#else
473+
""
474+
#endif
475+
} else {
476+
0
477+
}
478+
}
479+
480+
func testPoundIf2() -> String {
481+
if .random() {
482+
#if true
483+
0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
484+
#else
485+
""
486+
#endif
487+
} else {
488+
""
489+
}
490+
}
491+
492+
func testPoundIf3() -> String {
493+
if .random() {
494+
#if false
495+
0
496+
#else
497+
""
498+
#endif
499+
} else {
500+
""
501+
}
502+
}
503+
504+
func testPoundIf4() -> String {
505+
let x = if .random() {
506+
#if true
507+
0 // expected-error {{branches have mismatching types 'Int' and 'String'}}
508+
#else
509+
""
510+
#endif
511+
} else {
512+
""
513+
}
514+
return x
515+
}
516+
517+
func testPoundIf5() -> String {
518+
let x = if .random() {
519+
#if false
520+
0
521+
#else
522+
""
523+
#endif
524+
} else {
525+
""
526+
}
527+
return x
528+
}
529+
530+
func testPoundIfClosure1() -> Int {
531+
let fn = {
532+
if .random() {
533+
#if true
534+
0
535+
#else
536+
""
537+
#endif
538+
} else {
539+
0
540+
}
541+
}
542+
return fn()
543+
}
544+
545+
func testPoundIfClosure2() -> String {
546+
let fn: () -> String = {
547+
if .random() {
548+
#if true
549+
0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
550+
#else
551+
""
552+
#endif
553+
} else {
554+
""
555+
}
556+
}
557+
return fn()
558+
}
559+
466560
// MARK: Subtyping
467561

468562
class A {}

test/Constraints/switch_expr.swift

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,107 @@ func testThrowInference() {
545545
}
546546
}
547547

548+
// MARK: Pound if
549+
550+
func testPoundIf1() -> Int {
551+
switch Bool.random() {
552+
case true:
553+
#if true
554+
0
555+
#else
556+
""
557+
#endif
558+
case false:
559+
0
560+
}
561+
}
562+
563+
func testPoundIf2() -> String {
564+
switch Bool.random() {
565+
case true:
566+
#if true
567+
0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
568+
#else
569+
""
570+
#endif
571+
case false:
572+
""
573+
}
574+
}
575+
576+
func testPoundIf3() -> String {
577+
switch Bool.random() {
578+
case true:
579+
#if false
580+
0
581+
#else
582+
""
583+
#endif
584+
case false:
585+
""
586+
}
587+
}
588+
589+
func testPoundIf4() -> String {
590+
let x = switch Bool.random() {
591+
case true:
592+
#if true
593+
0 // expected-error {{branches have mismatching types 'Int' and 'String'}}
594+
#else
595+
""
596+
#endif
597+
case false:
598+
""
599+
}
600+
return x
601+
}
602+
603+
func testPoundIf5() -> String {
604+
let x = switch Bool.random() {
605+
case true:
606+
#if false
607+
0
608+
#else
609+
""
610+
#endif
611+
case false:
612+
""
613+
}
614+
return x
615+
}
616+
617+
func testPoundIfClosure1() -> Int {
618+
let fn = {
619+
switch Bool.random() {
620+
case true:
621+
#if true
622+
0
623+
#else
624+
""
625+
#endif
626+
case false:
627+
0
628+
}
629+
}
630+
return fn()
631+
}
632+
633+
func testPoundIfClosure2() -> String {
634+
let fn: () -> String = {
635+
switch Bool.random() {
636+
case true:
637+
#if true
638+
0 // expected-error {{cannot convert value of type 'Int' to specified type 'String'}}
639+
#else
640+
""
641+
#endif
642+
case false:
643+
""
644+
}
645+
}
646+
return fn()
647+
}
648+
548649
// MARK: Subtyping
549650

550651
class A {}

test/SILGen/if_expr.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,39 @@ func testVar() -> Int {
196196
return x
197197
}
198198

199+
func testPoundIf1() -> Int {
200+
let x = if .random() {
201+
#if true
202+
1
203+
#else
204+
""
205+
#endif
206+
} else {
207+
#if false
208+
""
209+
#else
210+
2
211+
#endif
212+
}
213+
return x
214+
}
215+
216+
func testPoundIf2() -> Int {
217+
if .random() {
218+
#if false
219+
0
220+
#else
221+
#if true
222+
if .random() { 0 } else { 1 }
223+
#endif
224+
#endif
225+
} else {
226+
#if true
227+
if .random() { 0 } else { 1 }
228+
#endif
229+
}
230+
}
231+
199232
func testCatch() -> Int {
200233
do {
201234
let x = if .random() {

0 commit comments

Comments
 (0)