Files
swift-mirror/test/SILGen/switch_fallthrough.swift
Doug Gregor 7f7530f4e7 [Statement checking] Fix "old" fallthrough source.
Fallthrough statement sources have always been incorrectly computed
when there are nested switch statements. The recent refactoring to
switch fallthrough source/destination computation over to ASTScope
fixed the computation. Amusingly, the assertion that ensures that the
old and new implementations produce the same result fires on these
cases, but it's the old implementation that's wrong. Fix up the old
implementation so the assertion does not trigger. The new test case
crashes in Swift 5.3 and earlier, asserts prior to this change.

Fixes rdar://problem/67704651.
2020-08-31 16:11:55 -07:00

185 lines
5.0 KiB
Swift

// RUN: %target-swift-emit-silgen %s | %FileCheck %s
// Some fake predicates for pattern guards.
func runced() -> Bool { return true }
func funged() -> Bool { return true }
func ansed() -> Bool { return true }
func foo() -> Int { return 0 }
func bar() -> Int { return 0 }
func a() {}
func b() {}
func c() {}
func d() {}
func e() {}
func f() {}
func g() {}
func z(_ i: Int) {}
// CHECK-LABEL: sil hidden [ossa] @$s18switch_fallthrough5test1yyF
func test1() {
switch foo() {
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], {{bb[0-9]+}}
// CHECK: [[YES_CASE1]]:
case foo():
// CHECK: function_ref @$s18switch_fallthrough1ayyF
// CHECK: br [[CASE2:bb[0-9]+]]
a()
fallthrough
case bar():
// CHECK: [[CASE2]]:
// CHECK: function_ref @$s18switch_fallthrough1byyF
// CHECK: br [[CASE3:bb[0-9]+]]
b()
fallthrough
case _:
// CHECK: [[CASE3]]:
// CHECK: function_ref @$s18switch_fallthrough1cyyF
c()
}
// CHECK: function_ref @$s18switch_fallthrough1dyyF
d()
}
// Fallthrough should work even if the next case is normally unreachable
// CHECK-LABEL: sil hidden [ossa] @$s18switch_fallthrough5test2yyF
func test2() {
switch foo() {
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], {{bb[0-9]+}}
// CHECK: [[YES_CASE1]]:
case foo():
// CHECK: function_ref @$s18switch_fallthrough1ayyF
// CHECK: br [[CASE2:bb[0-9]+]]
a()
fallthrough
case _:
// CHECK: [[CASE2]]:
// CHECK: function_ref @$s18switch_fallthrough1byyF
// CHECK: br [[CASE3:bb[0-9]+]]
b()
fallthrough
case _:
// CHECK: [[CASE3]]:
// CHECK: function_ref @$s18switch_fallthrough1cyyF
c()
}
// CHECK: function_ref @$s18switch_fallthrough1dyyF
d()
}
// CHECK-LABEL: sil hidden [ossa] @$s18switch_fallthrough5test3yyF
func test3() {
switch (foo(), bar()) {
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], {{bb[0-9]+}}
// CHECK: [[YES_CASE1]]:
case (foo(), bar()):
// CHECK: function_ref @$s18switch_fallthrough1ayyF
// CHECK: br [[CASE2:bb[0-9]+]]
a()
fallthrough
case (foo(), _):
// CHECK: [[CASE2]]:
// CHECK: function_ref @$s18switch_fallthrough1byyF
// CHECK: br [[CASE3:bb[0-9]+]]
b()
fallthrough
case (_, _):
// CHECK: [[CASE3]]:
// CHECK: function_ref @$s18switch_fallthrough1cyyF
// CHECK: br [[CASE4:bb[0-9]+]]
c()
fallthrough
case (_, _):
// CHECK: [[CASE4]]:
// CHECK: function_ref @$s18switch_fallthrough1dyyF
d()
}
// CHECK: function_ref @$s18switch_fallthrough1eyyF
e()
}
// Fallthrough should clean up nested pattern variables from the exited scope.
func test4() {
switch (foo(), bar()) {
// CHECK: [[A:%.*]] = alloc_box ${ var (Int, Int) }
// CHECK: cond_br {{%.*}}, [[CASE1:bb[0-9]+]], {{bb[0-9]+}}
case var a where runced():
// CHECK: [[CASE1]]:
// CHECK: br [[CASE2:bb[0-9]+]]
fallthrough
case _ where funged():
// CHECK: [[CASE2]]:
// CHECK: br [[CONT:bb[0-9]+]]
()
// CHECK: [[B:%.*]] = alloc_box ${ var Int }
// CHECK: [[C:%.*]] = alloc_box ${ var Int }
// CHECK: cond_br {{%.*}}, [[CASE4:bb[0-9]+]],
case (var b, var c) where ansed():
// CHECK: [[CASE4]]:
// CHECK: br [[CASE5:bb[0-9]+]]
fallthrough
case _:
// CHECK: [[CASE5]]:
// CHECK: br [[CONT]]
()
}
// CHECK: [[CONT]]:
// CHECK-NEXT: tuple ()
// CHECK-NEXT: return
}
// Fallthrough into case block with binding // CHECK-LABEL: sil hidden [ossa] @$s18switch_fallthrough5test5yyF
func test5() {
switch (foo(), bar()) {
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], {{bb[0-9]+}}
// CHECK: [[YES_CASE1]]:
case (var n, foo()):
// Check that the var is boxed and unboxed and the final value is the one that falls through into the next case
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int }, var, name "n"
// CHECK: [[N_BOX:%.*]] = project_box [[BOX]] : ${ var Int }, 0
// CHECK: function_ref @$s18switch_fallthrough1ayyF
// CHECK: [[N:%.*]] = load [trivial] [[N_BOX]] : $*Int
// CHECK: destroy_value [[BOX]] : ${ var Int }
// CHECK: br [[CASE2:bb[0-9]+]]([[N]] : $Int)
a()
fallthrough
case (foo(), let n):
// CHECK: cond_br {{%.*}}, [[YES_SECOND_CONDITION:bb[0-9]+]], {{bb[0-9]+}}
// CHECK: [[YES_SECOND_CONDITION]]:
// CHECK: br [[CASE2]]([[SECOND_N:%.*]] : $Int)
// CHECK: [[CASE2]]([[INCOMING_N:%.*]] : $Int):
// CHECK: debug_value [[INCOMING_N]] : $Int, let, name "n"
// CHECK: [[Z:%.*]] = function_ref @$s18switch_fallthrough1zyySiF
// CHECK: apply [[Z]]([[INCOMING_N]]) : $@convention(thin) (Int) -> ()
// CHECK: br [[CONT:bb[0-9]+]]
z(n)
case (_, _):
break
}
// CHECK: [[CONT]]:
// CHECK: function_ref @$s18switch_fallthrough1eyyF
e()
}
// rdar://problem/67704651 - crash due to nested fallthrough
func testNestedFallthrough(x: (Int, String), y: (Int, Int)) {
switch x {
case (17, let s):
switch y {
case (42, let i):
print("the answer")
default:
print("nope")
}
fallthrough
case (42, let s):
print("42 and \(s)")
default:
print("done")
}
}