Skip to content

Commit 769805e

Browse files
committed
[Sema] Fully allow fallthrough in switch expressions
Allow `fallthrough` to appear as the last statement in the case of a `switch` expression. We already allowed it in other positions, this was just an oversight. rdar://127670432
1 parent 55aed16 commit 769805e

File tree

6 files changed

+137
-29
lines changed

6 files changed

+137
-29
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,8 +1328,9 @@ ERROR(single_value_stmt_branch_empty,none,
13281328
"expected expression in branch of '%0' expression",
13291329
(StmtKind))
13301330
ERROR(single_value_stmt_branch_must_end_in_result,none,
1331-
"non-expression branch of '%0' expression may only end with a 'throw'",
1332-
(StmtKind))
1331+
"non-expression branch of '%0' expression may only end with a 'throw'"
1332+
"%select{| or 'fallthrough'}1",
1333+
(StmtKind, bool))
13331334
ERROR(cannot_jump_in_single_value_stmt,none,
13341335
"cannot use '%0' to transfer control out of '%1' expression",
13351336
(StmtKind, StmtKind))

lib/Sema/MiscDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4478,7 +4478,7 @@ class SingleValueStmtUsageChecker final : public ASTWalker {
44784478
// default.
44794479
Diags.diagnose(branch->getEndLoc(),
44804480
diag::single_value_stmt_branch_must_end_in_result,
4481-
S->getKind());
4481+
S->getKind(), isa<SwitchStmt>(S));
44824482
}
44834483
break;
44844484
}

lib/Sema/TypeCheckStmt.cpp

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3071,18 +3071,6 @@ class JumpOutOfContextFinder : public ASTWalker {
30713071
};
30723072
} // end anonymous namespace
30733073

3074-
/// Whether the given brace statement ends with a 'throw'.
3075-
static bool doesBraceEndWithThrow(BraceStmt *BS) {
3076-
if (BS->empty())
3077-
return false;
3078-
3079-
auto *S = BS->getLastElement().dyn_cast<Stmt *>();
3080-
if (!S)
3081-
return false;
3082-
3083-
return isa<ThrowStmt>(S);
3084-
}
3085-
30863074
IsSingleValueStmtResult
30873075
areBranchesValidForSingleValueStmt(ASTContext &ctx, ArrayRef<Stmt *> branches) {
30883076
TinyPtrVector<Stmt *> invalidJumps;
@@ -3108,8 +3096,20 @@ areBranchesValidForSingleValueStmt(ASTContext &ctx, ArrayRef<Stmt *> branches) {
31083096
continue;
31093097
}
31103098

3111-
// If there was no result, the branch must end in a 'throw'.
3112-
if (!doesBraceEndWithThrow(BS))
3099+
// If there was no result, the branch must end in a 'throw' or
3100+
// 'fallthrough'.
3101+
auto endsInJump = [&]() {
3102+
if (BS->empty())
3103+
return false;
3104+
3105+
auto *S = BS->getLastElement().dyn_cast<Stmt *>();
3106+
if (!S)
3107+
return false;
3108+
3109+
return isa<ThrowStmt>(S) || isa<FallthroughStmt>(S);
3110+
}();
3111+
3112+
if (!endsInJump)
31133113
unterminatedBranches.push_back(BS);
31143114
}
31153115

test/SILGen/switch_expr.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,35 @@ func testFallthrough() throws -> Int {
168168
// CHECK: dealloc_stack [[RESULT]] : $*Int
169169
// CHECK: return [[VAL]] : $Int
170170

171+
func testFallthrough2() -> Int {
172+
let x = switch Bool.random() {
173+
case true:
174+
fallthrough
175+
case false:
176+
1
177+
}
178+
return x
179+
}
180+
181+
// CHECK-LABEL: sil hidden [ossa] @$s11switch_expr16testFallthrough2SiyF : $@convention(thin) () -> Int {
182+
// CHECK: [[RESULT:%[0-9]+]] = alloc_stack $Int
183+
// CHECK: switch_value {{%[0-9]+}} : $Builtin.Int1, case {{%[0-9]+}}: [[TRUEBB:bb[0-9]+]], case {{%[0-9]+}}: [[FALSEBB:bb[0-9]+]]
184+
//
185+
// CHECK: [[TRUEBB]]:
186+
// CHECK-NEXT: br [[ENDBB:bb[0-9]+]]
187+
//
188+
// CHECK: [[FALSEBB]]:
189+
// CHECK-NEXT: br [[ENDBB]]
190+
//
191+
// CHECK: [[ENDBB]]:
192+
// CHECK: [[ONELIT:%[0-9]+]] = integer_literal $Builtin.IntLiteral, 1
193+
// CHECK: [[ONE:%[0-9]+]] = apply {{%[0-9]+}}([[ONELIT]], {{%[0-9]+}})
194+
// CHECK: store [[ONE]] to [trivial] [[RESULT]] : $*Int
195+
// CHECK: [[VAL:%[0-9]+]] = load [trivial] [[RESULT]] : $*Int
196+
// CHECK: [[VAL_RESULT:%[0-9]+]] = move_value [var_decl] [[VAL]] : $Int
197+
// CHECK: dealloc_stack [[RESULT]] : $*Int
198+
// CHECK: return [[VAL_RESULT:[%0-9]+]] : $Int
199+
171200
func testClosure() throws -> Int {
172201
let fn = {
173202
switch Bool.random() {

test/expr/unary/switch_expr.swift

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ func returnBranches6() -> Int {
820820
case true:
821821
print("hello")
822822
0 // expected-warning {{integer literal is unused}}
823-
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw'}}
823+
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
824824
case false:
825825
1
826826
}
@@ -835,7 +835,7 @@ func returnBranches6PoundIf() -> Int {
835835
print("hello")
836836
0 // expected-warning {{integer literal is unused}}
837837
#endif
838-
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw'}}
838+
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
839839
case false:
840840
1
841841
}
@@ -850,7 +850,7 @@ func returnBranches6PoundIf2() -> Int {
850850
print("hello")
851851
0
852852
#endif
853-
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw'}}
853+
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
854854
case false:
855855
1
856856
}
@@ -882,7 +882,7 @@ func returnBranches9() -> Int {
882882
let i = switch Bool.random() {
883883
case true:
884884
print("hello")
885-
if .random() {} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw'}}
885+
if .random() {} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
886886
case false:
887887
1
888888
}
@@ -898,7 +898,7 @@ func returnBranches10() -> Int {
898898
0 // expected-warning {{integer literal is unused}}
899899
case false:
900900
2 // expected-warning {{integer literal is unused}}
901-
} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw'}}
901+
} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
902902
case false:
903903
1
904904
}
@@ -914,7 +914,7 @@ func returnBranches11() -> Int {
914914
"" // expected-warning {{string literal is unused}}
915915
case false:
916916
2 // expected-warning {{integer literal is unused}}
917-
} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw'}}
917+
} // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
918918
case false:
919919
1
920920
}
@@ -1059,7 +1059,7 @@ func testPoundIfBranch3() -> Int {
10591059
#if false
10601060
0
10611061
#endif
1062-
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw'}}
1062+
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
10631063
case false:
10641064
0
10651065
}
@@ -1100,7 +1100,7 @@ func testPoundIfBranch6() -> Int {
11001100
0
11011101
#endif
11021102
0 // expected-warning {{integer literal is unused}}
1103-
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw'}}
1103+
// expected-error@-1 {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
11041104
case false:
11051105
1
11061106
}
@@ -1180,6 +1180,38 @@ func fallthrough2() -> Int {
11801180
return x
11811181
}
11821182

1183+
func fallthrough3() -> Int {
1184+
let x = switch true {
1185+
case true:
1186+
fallthrough
1187+
case false:
1188+
0
1189+
}
1190+
return x
1191+
}
1192+
1193+
func fallthrough4() -> Int {
1194+
let x = switch true {
1195+
case true:
1196+
fallthrough
1197+
return 0 // expected-error {{cannot use 'return' to transfer control out of 'switch' expression}}
1198+
case false:
1199+
0
1200+
}
1201+
return x
1202+
}
1203+
1204+
func fallthrough5() -> Int {
1205+
let x = switch true {
1206+
case true:
1207+
fallthrough
1208+
print(0) // expected-error {{non-expression branch of 'switch' expression may only end with a 'throw' or 'fallthrough'}}
1209+
case false:
1210+
0
1211+
}
1212+
return x
1213+
}
1214+
11831215
func breakAfterNeverExpr() -> String {
11841216
// We avoid turning this into a switch expression because of the 'break'.
11851217
switch Bool.random() {

test/stmt/then_stmt_exec.swift

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
// Required for experimental features
99
// REQUIRES: asserts
1010

11-
func testDefer(_ x: Bool) -> Int {
11+
func testDeferIf(_ x: Bool) -> Int {
1212
defer {
13-
print("defer fn")
13+
print("defer testDeferIf")
1414
}
1515
let x = if x {
1616
defer {
@@ -24,9 +24,55 @@ func testDefer(_ x: Bool) -> Int {
2424
print("after if")
2525
return x
2626
}
27-
_ = testDefer(true)
27+
_ = testDeferIf(true)
2828

2929
// CHECK: enter if
3030
// CHECK-NEXT: defer if
3131
// CHECK-NEXT: after if
32-
// CHECK-NEXT: defer fn
32+
// CHECK-NEXT: defer testDeferIf
33+
34+
func testDeferSwitch(_ x: Bool) -> Int {
35+
defer {
36+
print("defer testDeferSwitch")
37+
}
38+
let x = switch x {
39+
case true:
40+
defer {
41+
print("defer case true")
42+
}
43+
print("enter case true")
44+
fallthrough
45+
case false:
46+
defer {
47+
print("defer case false")
48+
}
49+
print("enter case false")
50+
then 1
51+
}
52+
print("after switch")
53+
return x
54+
}
55+
_ = testDeferSwitch(true)
56+
// CHECK: enter case true
57+
// CHECK-NEXT: defer case true
58+
// CHECK-NEXT: enter case false
59+
// CHECK-NEXT: defer case false
60+
// CHECK-NEXT: after switch
61+
// CHECK-NEXT: defer testDeferSwitch
62+
63+
func testFallthrough(_ x: Bool) -> Int {
64+
var z = 0
65+
let y: Int
66+
let x = switch x {
67+
case true:
68+
z = 1
69+
fallthrough
70+
case false:
71+
y = 2
72+
then 3
73+
}
74+
return x + y + z
75+
}
76+
print("fallthrough: \(testFallthrough(true))")
77+
78+
// CHECK: fallthrough: 6

0 commit comments

Comments
 (0)