mirror of
https://github.com/apple/swift.git
synced 2026-06-27 12:25:55 +02:00
097b0d3400
We cannot use spare bits or other overlapping storage layout tricks with fundamentally address-only enums, and we can take advantage of this to do borrowing switches or other in-place projections without copying the value. However, for resilient enums, the implementation may use spare bit packing, but the type must be handled address-only outside of its defining module, and we didn't have a way to express that with borrowing switch. Optimization passes have also been running into problems with the complexity that we were using `unchecked_take_enum_data_addr` sometimes as a pure operation. This patch splits the instruction into three: - `unchecked_inplace_enum_data_addr` represents a nondestructive in-place enum projection. It is only allowed for enums whose projection operation is nondestructive. - `unchecked_take_enum_data_addr` represents a destructive enum projection, invalidating the enum and leaving the payload to be further consumed. This matches the current instruction's semantics. - `unchecked_borrow_enum_data_addr` represents a borrowing enum projection. The instruction takes a second operand for "scratch" space, which the enum representation may be copied into in order to avoid invalidating the enum value, so the result is dependent on the lifetime of both the original enum and the scratch buffer. This allows for borrowing switches over resilient enums. `unchecked_borrow_enum_data_addr` is implemented by taking advantage of the "address-only enums can't do spare bit optimization" property at runtime. We inspect the operand type's bitwise-borrowability from its metadata. If the type is bitwise-borrowable, then we are allowed to bitwise-copy the enum to the scratch space and apply the projection to the scratch space, preserving the original value. If the type is not bitwise-borrowable, then we cannot use spare bit optimization in its layout, so we apply the projection in-place. Fixes rdar://174952822.
1757 lines
59 KiB
Swift
1757 lines
59 KiB
Swift
// RUN: %target-swift-emit-silgen -Xllvm -sil-print-types -module-name switch %s | %FileCheck %s
|
|
|
|
//////////////////
|
|
// Declarations //
|
|
//////////////////
|
|
|
|
func markUsed<T>(_ t: T) {}
|
|
|
|
// TODO: Implement tuple equality in the library.
|
|
// BLOCKED: <rdar://problem/13822406>
|
|
func ~= (x: (Int, Int), y: (Int, Int)) -> Bool {
|
|
return x.0 == y.0 && x.1 == y.1
|
|
}
|
|
|
|
// 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 foobar() -> (Int, Int) { return (0, 0) }
|
|
|
|
func a() {}
|
|
func b() {}
|
|
func c() {}
|
|
func d() {}
|
|
func e() {}
|
|
func f() {}
|
|
func g() {}
|
|
|
|
func a(_ k: Klass) {}
|
|
func b(_ k: Klass) {}
|
|
func c(_ k: Klass) {}
|
|
func d(_ k: Klass) {}
|
|
func e(_ k: Klass) {}
|
|
func f(_ k: Klass) {}
|
|
func g(_ k: Klass) {}
|
|
|
|
class Klass {
|
|
var isTrue: Bool { return true }
|
|
var isFalse: Bool { return false }
|
|
}
|
|
|
|
enum TrivialSingleCaseEnum {
|
|
case a
|
|
}
|
|
|
|
enum NonTrivialSingleCaseEnum {
|
|
case a(Klass)
|
|
}
|
|
|
|
enum MultipleNonTrivialCaseEnum {
|
|
case a(Klass)
|
|
case b(Klass)
|
|
case c(Klass)
|
|
}
|
|
|
|
enum MultipleAddressOnlyCaseEnum<T : BinaryInteger> {
|
|
case a(T)
|
|
case b(T)
|
|
case c(T)
|
|
}
|
|
|
|
///////////
|
|
// Tests //
|
|
///////////
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test1yyF
|
|
func test1() {
|
|
switch foo() {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
case _:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
}
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test2yyF
|
|
func test2() {
|
|
switch foo() {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
case _:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
case _: // The second case is unreachable.
|
|
b()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test3yyF
|
|
func test3() {
|
|
switch foo() {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch6runcedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE1:bb[0-9]+]], [[CASE2:bb[0-9]+]]
|
|
|
|
case _ where runced():
|
|
// CHECK: [[CASE1]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
case _:
|
|
// CHECK: [[CASE2]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test4yyF
|
|
func test4() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
case _:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
}
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test5yyF
|
|
func test5() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: function_ref @$s6switch6runcedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE1:bb[0-9]+]], [[NOT_CASE1:bb[0-9]+]]
|
|
case _ where runced():
|
|
// CHECK: [[CASE1]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[NOT_CASE1]]:
|
|
// CHECK: function_ref @$s6switch6fungedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[YES_CASE2:bb[0-9]+]], [[NOT_CASE2:bb[0-9]+]]
|
|
// CHECK: [[YES_CASE2]]:
|
|
case (_, _) where funged():
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
case _:
|
|
// CHECK: [[NOT_CASE2]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
}
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
d()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test6yyF
|
|
func test6() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
case (_, _):
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
case (_, _): // The second case is unreachable.
|
|
b()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test7yyF
|
|
func test7() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: function_ref @$s6switch6runcedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], [[NOT_CASE1:bb[0-9]+]]
|
|
// CHECK: [[YES_CASE1]]:
|
|
case (_, _) where runced():
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
case (_, _):
|
|
// CHECK: [[NOT_CASE1]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
}
|
|
c()
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test8yyF
|
|
func test8() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: function_ref @$s6switch6foobarSi_SityF
|
|
// CHECK: cond_br {{%.*}}, [[CASE1:bb[0-9]+]], [[NOT_CASE1:bb[0-9]+]]
|
|
case foobar():
|
|
// CHECK: [[CASE1]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[NOT_CASE1]]:
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE2:bb[0-9]+]], [[NOT_CASE2:bb[0-9]+]]
|
|
case (foo(), _):
|
|
// CHECK: [[CASE2]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
// CHECK: [[NOT_CASE2]]:
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE3_GUARD:bb[0-9]+]], [[NOT_CASE3:bb[0-9]+]]
|
|
// CHECK: [[CASE3_GUARD]]:
|
|
// CHECK: function_ref @$s6switch6runcedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE3:bb[0-9]+]], [[NOT_CASE3_GUARD:bb[0-9]+]]
|
|
case (_, bar()) where runced():
|
|
// CHECK: [[CASE3]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
// CHECK: [[NOT_CASE3_GUARD]]:
|
|
// CHECK: [[NOT_CASE3]]:
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE4_GUARD_1:bb[0-9]+]], [[NOT_CASE4_1:bb[0-9]+]]
|
|
// CHECK: [[CASE4_GUARD_1]]:
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE4_GUARD_2:bb[0-9]+]], [[NOT_CASE4_2:bb[0-9]+]]
|
|
// CHECK: [[CASE4_GUARD_2]]:
|
|
// CHECK: function_ref @$s6switch6fungedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE4:bb[0-9]+]], [[NOT_CASE4_3:bb[0-9]+]]
|
|
case (foo(), bar()) where funged():
|
|
// CHECK: [[CASE4]]:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
// CHECK: [[NOT_CASE4_3]]:
|
|
// CHECK: [[NOT_CASE4_2]]:
|
|
// CHECK: [[NOT_CASE4_1]]:
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE5_GUARD_1:bb[0-9]+]], [[NOT_CASE5:bb[0-9]+]]
|
|
// CHECK: [[CASE5_GUARD_1]]:
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: cond_br {{%.*}}, [[YES_CASE5:bb[0-9]+]], [[NOT_CASE5:bb[0-9]+]]
|
|
// CHECK: [[YES_CASE5]]:
|
|
case (foo(), bar()):
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
// CHECK: br [[CONT]]
|
|
e()
|
|
// CHECK: [[NOT_CASE5]]:
|
|
// CHECK: br [[CASE6:bb[0-9]+]]
|
|
case _:
|
|
// CHECK: [[CASE6]]:
|
|
// CHECK: function_ref @$s6switch1fyyF
|
|
// CHECK: br [[CONT]]
|
|
f()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1gyyF
|
|
g()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch5test9yyF
|
|
func test9() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], [[NOT_CASE1:bb[0-9]+]]
|
|
// CHECK: [[YES_CASE1]]:
|
|
case (foo(), _):
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[NOT_CASE1]]:
|
|
// CHECK: function_ref @$s6switch6foobarSi_SityF
|
|
// CHECK: cond_br {{%.*}}, [[CASE2:bb[0-9]+]], [[NOT_CASE2:bb[0-9]+]]
|
|
case foobar():
|
|
// CHECK: [[CASE2]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
case _:
|
|
// CHECK: [[NOT_CASE2]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
}
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
d()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch6test10yyF
|
|
func test10() {
|
|
switch (foo(), bar()) {
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], [[NOT_CASE1:bb[0-9]+]]
|
|
// CHECK: [[YES_CASE1]]:
|
|
case (foo()...bar(), _):
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
case _:
|
|
// CHECK: [[NOT_CASE1]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
}
|
|
|
|
protocol P { func p() }
|
|
|
|
struct X : P { func p() {} }
|
|
struct Y : P { func p() {} }
|
|
struct Z : P { func p() {} }
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch10test_isa_11pyAA1P_p_tF
|
|
func test_isa_1(p: P) {
|
|
// CHECK: [[PTMPBUF:%[0-9]+]] = alloc_stack $any P
|
|
// CHECK-NEXT: copy_addr %0 to [init] [[PTMPBUF]] : $*any P
|
|
switch p {
|
|
// CHECK: [[TMPBUF:%[0-9]+]] = alloc_stack $X
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P:%.*]] : $*any P to X in [[TMPBUF]] : $*X, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
|
|
|
|
case is X:
|
|
// CHECK: [[IS_X]]:
|
|
// CHECK-NEXT: load [trivial] [[TMPBUF]]
|
|
// CHECK-NEXT: // function_ref
|
|
// CHECK-NEXT: [[FUNC:%.*]] = function_ref @$s6switch1ayyF
|
|
// CHECK-NEXT: apply [[FUNC]]()
|
|
// CHECK-NEXT: dealloc_stack [[TMPBUF]]
|
|
// CHECK-NEXT: destroy_addr [[PTMPBUF]]
|
|
// CHECK-NEXT: dealloc_stack [[PTMPBUF]]
|
|
a()
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_X]]:
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P]] : $*any P to Y in {{%.*}} : $*Y, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_Y]]:
|
|
case is Y:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[Y_CONT:bb[0-9]+]]
|
|
b()
|
|
|
|
// CHECK: [[IS_NOT_Y]]:
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P]] : $*any P to Z in {{%.*}} : $*Z, [[IS_Z:bb[0-9]+]], [[IS_NOT_Z:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_Z]]:
|
|
case is Z:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[Z_CONT:bb[0-9]+]]
|
|
c()
|
|
|
|
// CHECK: [[IS_NOT_Z]]:
|
|
case _:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
e()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch10test_isa_21pyAA1P_p_tF
|
|
func test_isa_2(p: P) {
|
|
switch (p, foo()) {
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P:%.*]] : $*any P to X in {{%.*}} : $*X, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_X]]:
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE1:bb[0-9]+]], [[NOT_CASE1:bb[0-9]+]]
|
|
case (is X, foo()):
|
|
// CHECK: [[CASE1]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[IS_NOT_X]]:
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P]] : $*any P to Y in {{%.*}} : $*Y, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_Y]]:
|
|
// CHECK: function_ref @$s6switch3fooSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE2:bb[0-9]+]], [[NOT_CASE2:bb[0-9]+]]
|
|
case (is Y, foo()):
|
|
// CHECK: [[CASE2]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[NOT_CASE2]]:
|
|
// CHECK: [[IS_NOT_Y]]:
|
|
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P:%.*]] : $*any P to X in {{%.*}} : $*X, [[CASE3:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
|
|
|
|
case (is X, _):
|
|
// CHECK: [[CASE3]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// -- case (is Y, foo()):
|
|
// CHECK: [[IS_NOT_X]]:
|
|
// CHECK: checked_cast_addr_br copy_on_success any P in [[P]] : $*any P to Y in {{%.*}} : $*Y, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
|
|
// CHECK: [[IS_Y]]:
|
|
// CHECK: function_ref @$s6switch3barSiyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE4:bb[0-9]+]], [[NOT_CASE4:bb[0-9]+]]
|
|
case (is Y, bar()):
|
|
// CHECK: [[CASE4]]:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
|
|
// CHECK: [[NOT_CASE4]]:
|
|
// CHECK: br [[CASE5:bb[0-9]+]]
|
|
// CHECK: [[IS_NOT_Y]]:
|
|
// CHECK: br [[CASE5]]
|
|
case _:
|
|
// CHECK: [[CASE5]]:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
// CHECK: br [[CONT]]
|
|
e()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1fyyF
|
|
f()
|
|
}
|
|
|
|
class B {}
|
|
class C : B {}
|
|
class D1 : C {}
|
|
class D2 : D1 {}
|
|
class E : C {}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch16test_isa_class_11xyAA1BC_tF : $@convention(thin) (@guaranteed B) -> () {
|
|
func test_isa_class_1(x: B) {
|
|
// CHECK: bb0([[X:%.*]] : @guaranteed $B):
|
|
// CHECK: checked_cast_br B in [[X]] : $B to D1, [[IS_D1:bb[0-9]+]], [[IS_NOT_D1:bb[0-9]+]]
|
|
switch x {
|
|
|
|
// CHECK: [[IS_D1]]([[CAST_D1:%.*]] : @guaranteed $D1):
|
|
// CHECK: [[CAST_D1_COPY:%.*]] = copy_value [[CAST_D1]]
|
|
// CHECK: function_ref @$s6switch6runcedSbyF : $@convention(thin) () -> Bool
|
|
// CHECK: cond_br {{%.*}}, [[YES_CASE1:bb[0-9]+]], [[NO_CASE1:bb[0-9]+]]
|
|
|
|
// CHECK: [[YES_CASE1]]:
|
|
case is D1 where runced():
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: destroy_value [[CAST_D1_COPY]]
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[NO_CASE1]]:
|
|
// CHECK-NEXT: destroy_value [[CAST_D1_COPY]]
|
|
// CHECK: br [[NEXT_CASE:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_D1]]([[CASTFAIL_D1:%.*]] : @guaranteed $B):
|
|
// CHECK-NEXT: br [[NEXT_CASE]]
|
|
|
|
// CHECK: [[NEXT_CASE]]:
|
|
// CHECK: checked_cast_br B in [[X]] : $B to D2, [[IS_D2:bb[0-9]+]], [[IS_NOT_D2:bb[0-9]+]]
|
|
case is D2:
|
|
// CHECK: [[IS_D2]]([[CAST_D2:%.*]] : @guaranteed $D2):
|
|
// CHECK: [[CAST_D2_COPY:%.*]] = copy_value [[CAST_D2]]
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: destroy_value [[CAST_D2_COPY]]
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[IS_NOT_D2]]([[CASTFAIL_D2:%.*]] : @guaranteed $B):
|
|
// CHECK: checked_cast_br B in [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]]
|
|
case is E where funged():
|
|
// CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E):
|
|
// CHECK: [[CAST_E_COPY:%.*]] = copy_value [[CAST_E]]
|
|
// CHECK: function_ref @$s6switch6fungedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE3:bb[0-9]+]], [[NO_CASE3:bb[0-9]+]]
|
|
|
|
// CHECK: [[CASE3]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: destroy_value [[CAST_E_COPY]]
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// CHECK: [[NO_CASE3]]:
|
|
// CHECK-NEXT: destroy_value [[CAST_E_COPY]]
|
|
// CHECK: br [[NEXT_CASE:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_E]]([[NOTCAST_E:%.*]] : @guaranteed $B):
|
|
// CHECK: br [[NEXT_CASE]]
|
|
|
|
// CHECK: [[NEXT_CASE]]:
|
|
// CHECK: checked_cast_br B in [[X]] : $B to C, [[IS_C:bb[0-9]+]], [[IS_NOT_C:bb[0-9]+]]
|
|
|
|
case is C:
|
|
// CHECK: [[IS_C]]([[CAST_C:%.*]] : @guaranteed $C):
|
|
// CHECK: [[CAST_C_COPY:%.*]] = copy_value [[CAST_C]]
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK-NEXT: apply
|
|
// CHECK: destroy_value [[CAST_C_COPY]]
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
|
|
// CHECK: [[IS_NOT_C]]([[NOCAST_C:%.*]] : @guaranteed $B):
|
|
default:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
// CHECK: br [[CONT]]
|
|
e()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: [[F_FUNC:%.*]] = function_ref @$s6switch1fyyF : $@convention(thin) () -> ()
|
|
// CHECK: apply [[F_FUNC]]()
|
|
f()
|
|
}
|
|
// CHECK: } // end sil function '$s6switch16test_isa_class_11xyAA1BC_tF'
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch16test_isa_class_21xyXlAA1BC_tF : $@convention(thin)
|
|
func test_isa_class_2(x: B) -> AnyObject {
|
|
// CHECK: bb0([[X:%.*]] : @guaranteed $B):
|
|
switch x {
|
|
|
|
// CHECK: checked_cast_br B in [[X]] : $B to D1, [[IS_D1:bb[0-9]+]], [[IS_NOT_D1:bb[0-9]+]]
|
|
case let y as D1 where runced():
|
|
// CHECK: [[IS_D1]]([[CAST_D1:%.*]] : @guaranteed $D1):
|
|
// CHECK: [[CAST_D1_COPY:%.*]] = copy_value [[CAST_D1]]
|
|
// CHECK: [[MOVED_D1_COPY:%.*]] = move_value [lexical] [var_decl] [[CAST_D1_COPY]]
|
|
// CHECK: function_ref @$s6switch6runcedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE1:bb[0-9]+]], [[NO_CASE1:bb[0-9]+]]
|
|
|
|
// CHECK: [[CASE1]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: [[BORROWED_CAST_D1_COPY:%.*]] = begin_borrow [[MOVED_D1_COPY]]
|
|
// CHECK: [[CAST_D1_COPY_COPY:%.*]] = copy_value [[BORROWED_CAST_D1_COPY]]
|
|
// CHECK: [[RET:%.*]] = init_existential_ref [[CAST_D1_COPY_COPY]]
|
|
// CHECK: end_borrow [[BORROWED_CAST_D1_COPY]]
|
|
// CHECK: destroy_value [[MOVED_D1_COPY]]
|
|
// CHECK: br [[CONT:bb[0-9]+]]([[RET]] : $AnyObject)
|
|
a()
|
|
return y
|
|
|
|
// CHECK: [[NO_CASE1]]:
|
|
// CHECK: destroy_value [[MOVED_D1_COPY]]
|
|
// CHECK: br [[NEXT_CASE:bb5]]
|
|
|
|
// CHECK: [[IS_NOT_D1]]([[NOCAST_D1:%.*]] : @guaranteed $B):
|
|
// CHECK: br [[NEXT_CASE]]
|
|
|
|
// CHECK: [[NEXT_CASE]]:
|
|
// CHECK: checked_cast_br B in [[X]] : $B to D2, [[CASE2:bb[0-9]+]], [[IS_NOT_D2:bb[0-9]+]]
|
|
case let y as D2:
|
|
// CHECK: [[CASE2]]([[CAST_D2:%.*]] : @guaranteed $D2):
|
|
// CHECK: [[CAST_D2_COPY:%.*]] = copy_value [[CAST_D2]]
|
|
// CHECK: [[MOVED_CAST_D2_COPY:%.*]] = move_value [lexical] [var_decl] [[CAST_D2_COPY]]
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: [[BORROWED_CAST_D2_COPY:%.*]] = begin_borrow [[MOVED_CAST_D2_COPY]]
|
|
// CHECK: [[CAST_D2_COPY_COPY:%.*]] = copy_value [[BORROWED_CAST_D2_COPY]]
|
|
// CHECK: [[RET:%.*]] = init_existential_ref [[CAST_D2_COPY_COPY]]
|
|
// CHECK: end_borrow [[BORROWED_CAST_D2_COPY]]
|
|
// CHECK: destroy_value [[MOVED_CAST_D2_COPY]]
|
|
// CHECK: br [[CONT]]([[RET]] : $AnyObject)
|
|
b()
|
|
return y
|
|
|
|
// CHECK: [[IS_NOT_D2]]([[NOCAST_D2:%.*]] : @guaranteed $B):
|
|
// CHECK: checked_cast_br B in [[X]] : $B to E, [[IS_E:bb[0-9]+]], [[IS_NOT_E:bb[0-9]+]]
|
|
case let y as E where funged():
|
|
// CHECK: [[IS_E]]([[CAST_E:%.*]] : @guaranteed $E):
|
|
// CHECK: [[CAST_E_COPY:%.*]] = copy_value [[CAST_E]]
|
|
// CHECK: [[MOVED_CAST_E_COPY:%.*]] = move_value [lexical] [var_decl] [[CAST_E_COPY]]
|
|
// CHECK: function_ref @$s6switch6fungedSbyF
|
|
// CHECK: cond_br {{%.*}}, [[CASE3:bb[0-9]+]], [[NO_CASE3:bb[0-9]+]]
|
|
|
|
// CHECK: [[CASE3]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: [[BORROWED_CAST_E_COPY:%.*]] = begin_borrow [[MOVED_CAST_E_COPY]]
|
|
// CHECK: [[CAST_E_COPY_COPY:%.*]] = copy_value [[BORROWED_CAST_E_COPY]]
|
|
// CHECK: [[RET:%.*]] = init_existential_ref [[CAST_E_COPY_COPY]]
|
|
// CHECK: end_borrow [[BORROWED_CAST_E_COPY]]
|
|
// CHECK: destroy_value [[MOVED_CAST_E_COPY]]
|
|
// CHECK: br [[CONT]]([[RET]] : $AnyObject)
|
|
c()
|
|
return y
|
|
|
|
// CHECK: [[NO_CASE3]]:
|
|
// CHECK: destroy_value [[MOVED_CAST_E_COPY]]
|
|
// CHECK: br [[NEXT_CASE:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_E]]([[NOCAST_E:%.*]] : @guaranteed $B):
|
|
// CHECK: br [[NEXT_CASE]]
|
|
|
|
// CHECK: [[NEXT_CASE]]
|
|
// CHECK: checked_cast_br B in [[X]] : $B to C, [[CASE4:bb[0-9]+]], [[IS_NOT_C:bb[0-9]+]]
|
|
case let y as C:
|
|
// CHECK: [[CASE4]]([[CAST_C:%.*]] : @guaranteed $C):
|
|
// CHECK: [[CAST_C_COPY:%.*]] = copy_value [[CAST_C]]
|
|
// CHECK: [[MOVED_CAST_C_COPY:%.*]] = move_value [lexical] [var_decl] [[CAST_C_COPY]]
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: [[BORROWED_CAST_C_COPY:%.*]] = begin_borrow [[MOVED_CAST_C_COPY]]
|
|
// CHECK: [[CAST_C_COPY_COPY:%.*]] = copy_value [[BORROWED_CAST_C_COPY]]
|
|
// CHECK: [[RET:%.*]] = init_existential_ref [[CAST_C_COPY_COPY]]
|
|
// CHECK: end_borrow [[BORROWED_CAST_C_COPY]]
|
|
// CHECK: destroy_value [[MOVED_CAST_C_COPY]]
|
|
// CHECK: br [[CONT]]([[RET]] : $AnyObject)
|
|
d()
|
|
return y
|
|
|
|
// CHECK: [[IS_NOT_C]]([[NOCAST_C:%.*]] : @guaranteed $B):
|
|
default:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
// CHECK: [[X_COPY_2:%.*]] = copy_value [[X]]
|
|
// CHECK: [[RET:%.*]] = init_existential_ref [[X_COPY_2]]
|
|
// CHECK: br [[CONT]]([[RET]] : $AnyObject)
|
|
e()
|
|
return x
|
|
}
|
|
|
|
// CHECK: [[CONT]]([[T0:%.*]] : @owned $AnyObject):
|
|
// CHECK: return [[T0]]
|
|
}
|
|
// CHECK: } // end sil function '$s6switch16test_isa_class_21xyXlAA1BC_tF'
|
|
|
|
// https://github.com/apple/swift/issues/56139
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch31test_isa_pattern_array_downcast2psySayAA1P_pG_tF : $@convention(thin) (@guaranteed Array<any P>) -> () {
|
|
func test_isa_pattern_array_downcast(ps: Array<P>) {
|
|
// CHECK: bb0(%0 : @guaranteed $Array<any P>):
|
|
switch ps {
|
|
// CHECK: checked_cast_addr_br copy_on_success Array<any P> in [[P:%[0-9]+]] : $*Array<any P> to Array<X> in {{%[0-9]+}} : $*Array<X>, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
|
|
case is [X]:
|
|
// CHECK: [[IS_X]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_X]]:
|
|
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Array<Y>
|
|
// CHECK-NEXT: checked_cast_addr_br copy_on_success Array<any P> in [[P:%[0-9]+]] : $*Array<any P> to Array<Y> in [[DEST]] : $*Array<Y>, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
|
|
case let _ as [Y]:
|
|
// CHECK: [[IS_Y]]:
|
|
// CHECK-NEXT: load [take] [[DEST]] : $*Array<Y>
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
// CHECK: br [[CONT]]
|
|
default:
|
|
// CHECK: [[IS_NOT_Y]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
// CHECK: br [[CONT]]
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
d()
|
|
}
|
|
// CHECK: } // end sil function '$s6switch31test_isa_pattern_array_downcast2psySayAA1P_pG_tF'
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch39test_isa_pattern_array_downcast_closureyyF : $@convention(thin) () -> () {
|
|
// CHECK: function_ref @$s6switch39test_isa_pattern_array_downcast_closureyyFySayAA1P_pGcfU_
|
|
// CHECK: } // end sil function '$s6switch39test_isa_pattern_array_downcast_closureyyF'
|
|
func test_isa_pattern_array_downcast_closure() {
|
|
// CHECK-LABEL: sil private [ossa] @$s6switch39test_isa_pattern_array_downcast_closureyyFySayAA1P_pGcfU_ : $@convention(thin) (@guaranteed Array<any P>) -> () {
|
|
let _ = { (ps: [P]) -> Void in
|
|
// CHECK: bb0(%0 : @guaranteed $Array<any P>):
|
|
switch ps {
|
|
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Array<X>
|
|
// CHECK-NEXT: checked_cast_addr_br copy_on_success Array<any P> in [[P:%[0-9]+]] : $*Array<any P> to Array<X> in [[DEST]] : $*Array<X>, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
|
|
case let _ as [X]:
|
|
// CHECK: [[IS_X]]:
|
|
// CHECK-NEXT: load [take] [[DEST]] : $*Array<X>
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_X]]:
|
|
// CHECK: br [[DEF:bb[0-9]+]]
|
|
default:
|
|
// CHECK: [[DEF]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
// CHECK: br [[CONT]]
|
|
}
|
|
}
|
|
// CHECK: } // end sil function '$s6switch39test_isa_pattern_array_downcast_closureyyFySayAA1P_pGcfU_'
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch30test_isa_pattern_dict_downcast2psySDySSAA1P_pG_tF : $@convention(thin) (@guaranteed Dictionary<String, any P>) -> () {
|
|
func test_isa_pattern_dict_downcast(ps: Dictionary<String, P>) {
|
|
// CHECK: bb0(%0 : @guaranteed $Dictionary<String, any P>):
|
|
switch ps {
|
|
// CHECK: checked_cast_addr_br copy_on_success Dictionary<String, any P> in [[P:%[0-9]+]] : $*Dictionary<String, any P> to Dictionary<String, X> in {{%[0-9]+}} : $*Dictionary<String, X>, [[IS_X:bb[0-9]+]], [[IS_NOT_X:bb[0-9]+]]
|
|
case is [String : X]:
|
|
// CHECK: [[IS_X]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_X]]:
|
|
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Dictionary<String, Y>
|
|
// CHECK-NEXT: checked_cast_addr_br copy_on_success Dictionary<String, any P> in [[P:%[0-9]+]] : $*Dictionary<String, any P> to Dictionary<String, Y> in [[DEST]] : $*Dictionary<String, Y>, [[IS_Y:bb[0-9]+]], [[IS_NOT_Y:bb[0-9]+]]
|
|
case let _ as [String : Y]:
|
|
// CHECK: [[IS_Y]]:
|
|
// CHECK-NEXT: load [take] [[DEST]] : $*Dictionary<String, Y>
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
// CHECK: br [[CONT]]
|
|
default:
|
|
// CHECK: [[IS_NOT_Y]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
// CHECK: br [[CONT]]
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
d()
|
|
}
|
|
// CHECK-LABEL: } // end sil function '$s6switch30test_isa_pattern_dict_downcast2psySDySSAA1P_pG_tF'
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch29test_isa_pattern_set_downcast2psyShyxG_tSHRzlF : $@convention(thin) <T where T : Hashable> (@guaranteed Set<T>) -> () {
|
|
func test_isa_pattern_set_downcast<T: Hashable>(ps: Set<T>) {
|
|
// CHECK: bb0(%0 : @guaranteed $Set<T>):
|
|
switch ps {
|
|
// CHECK: checked_cast_addr_br copy_on_success Set<T> in [[P:%[0-9]+]] : $*Set<T> to Set<Int> in {{%[0-9]+}} : $*Set<Int>, [[IS_INT:bb[0-9]+]], [[IS_NOT_INT:bb[0-9]+]]
|
|
case is Set<Int>:
|
|
// CHECK: [[IS_INT]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NOT_INT]]:
|
|
// CHECK: [[DEST:%[0-9]+]] = alloc_stack $Set<Bool>
|
|
// CHECK-NEXT: checked_cast_addr_br copy_on_success Set<T> in [[P:%[0-9]+]] : $*Set<T> to Set<Bool> in [[DEST]] : $*Set<Bool>, [[IS_BOOL:bb[0-9]+]], [[IS_NOT_BOOL:bb[0-9]+]]
|
|
case let _ as Set<Bool>:
|
|
// CHECK: [[IS_BOOL]]:
|
|
// CHECK-NEXT: load [take] [[DEST]] : $*Set<Bool>
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
// CHECK: br [[CONT]]
|
|
default:
|
|
// CHECK: [[IS_NOT_BOOL]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
// CHECK: br [[CONT]]
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
d()
|
|
}
|
|
// CHECK: } // end sil function '$s6switch29test_isa_pattern_set_downcast2psyShyxG_tSHRzlF'
|
|
|
|
enum MaybePair {
|
|
case Neither
|
|
case Left(Int)
|
|
case Right(String)
|
|
case Both(Int, String)
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch12test_union_11uyAA9MaybePairO_tF
|
|
func test_union_1(u: MaybePair) {
|
|
switch u {
|
|
// CHECK: switch_enum [[SUBJECT:%.*]] : $MaybePair,
|
|
// CHECK: case #MaybePair.Neither!enumelt: [[IS_NEITHER:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Left!enumelt: [[IS_LEFT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Right!enumelt: [[IS_RIGHT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Both!enumelt: [[IS_BOTH:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NEITHER]]:
|
|
// CHECK-NOT: destroy_value
|
|
case .Neither:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[IS_LEFT]]({{%.*}}):
|
|
// CHECK-NOT: destroy_value
|
|
case (.Left):
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[IS_RIGHT]]([[STR:%.*]] : @guaranteed $String):
|
|
case var .Right:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// CHECK: [[IS_BOTH]]([[TUP:%.*]] : @guaranteed $(Int, String)):
|
|
case .Both:
|
|
// CHECK: ({{%.*}}, [[TUP_STR:%.*]]) = destructure_tuple [[TUP]]
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
}
|
|
|
|
// CHECK: [[CONT]]:
|
|
// CHECK-NOT: switch_enum [[SUBJECT]]
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
e()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch12test_union_31uyAA9MaybePairO_tF : $@convention(thin) (@guaranteed MaybePair) -> () {
|
|
func test_union_3(u: MaybePair) {
|
|
// CHECK: bb0([[ARG:%.*]] : @guaranteed $MaybePair):
|
|
// CHECK: switch_enum [[ARG]] : $MaybePair,
|
|
// CHECK: case #MaybePair.Neither!enumelt: [[IS_NEITHER:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Left!enumelt: [[IS_LEFT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Right!enumelt: [[IS_RIGHT:bb[0-9]+]],
|
|
// CHECK: default [[DEFAULT:bb[0-9]+]]
|
|
switch u {
|
|
// CHECK: [[IS_NEITHER]]:
|
|
case .Neither:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[IS_LEFT]]({{%.*}}):
|
|
case .Left:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[IS_RIGHT]]([[STR:%.*]] : @guaranteed $String):
|
|
case .Right:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// CHECK: [[DEFAULT]](
|
|
// -- Ensure the fully-opaque value is destroyed in the default case.
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
|
|
default:
|
|
d()
|
|
}
|
|
|
|
// CHECK: [[CONT]]:
|
|
// CHECK-NOT: switch_enum [[ARG]]
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
e()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch12test_union_41uyAA9MaybePairO_tF
|
|
func test_union_4(u: MaybePair) {
|
|
switch u {
|
|
// CHECK: switch_enum {{%.*}} : $MaybePair,
|
|
// CHECK: case #MaybePair.Neither!enumelt: [[IS_NEITHER:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Left!enumelt: [[IS_LEFT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Right!enumelt: [[IS_RIGHT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Both!enumelt: [[IS_BOTH:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NEITHER]]:
|
|
case .Neither:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[IS_LEFT]]({{%.*}}):
|
|
case .Left(_):
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[IS_RIGHT]]({{%.*}}):
|
|
case .Right(_):
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// CHECK: [[IS_BOTH]]({{%.*}}):
|
|
case .Both(_):
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
}
|
|
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
e()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch12test_union_51uyAA9MaybePairO_tF
|
|
func test_union_5(u: MaybePair) {
|
|
switch u {
|
|
// CHECK: switch_enum {{%.*}} : $MaybePair,
|
|
// CHECK: case #MaybePair.Neither!enumelt: [[IS_NEITHER:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Left!enumelt: [[IS_LEFT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Right!enumelt: [[IS_RIGHT:bb[0-9]+]],
|
|
// CHECK: case #MaybePair.Both!enumelt: [[IS_BOTH:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NEITHER]]:
|
|
case .Neither:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[IS_LEFT]]({{%.*}}):
|
|
case .Left(_):
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[IS_RIGHT]]({{%.*}}):
|
|
case .Right(_):
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// CHECK: [[IS_BOTH]]({{%.*}}):
|
|
case .Both(_, _):
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
}
|
|
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
e()
|
|
}
|
|
|
|
enum MaybeAddressOnlyPair {
|
|
case Neither
|
|
case Left(P)
|
|
case Right(String)
|
|
case Both(P, String)
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch22test_union_addr_only_11uyAA20MaybeAddressOnlyPairO_tF
|
|
func test_union_addr_only_1(u: MaybeAddressOnlyPair) {
|
|
switch u {
|
|
// CHECK: switch_enum_addr [[ENUM_ADDR:%.*]] : $*MaybeAddressOnlyPair,
|
|
// CHECK: case #MaybeAddressOnlyPair.Neither!enumelt: [[IS_NEITHER:bb[0-9]+]],
|
|
// CHECK: case #MaybeAddressOnlyPair.Left!enumelt: [[IS_LEFT:bb[0-9]+]],
|
|
// CHECK: case #MaybeAddressOnlyPair.Right!enumelt: [[IS_RIGHT:bb[0-9]+]],
|
|
// CHECK: case #MaybeAddressOnlyPair.Both!enumelt: [[IS_BOTH:bb[0-9]+]]
|
|
|
|
// CHECK: [[IS_NEITHER]]:
|
|
case .Neither:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: br [[CONT:bb[0-9]+]]
|
|
a()
|
|
|
|
// CHECK: [[IS_LEFT]]:
|
|
// CHECK: [[P:%.*]] = unchecked_inplace_enum_data_addr [[ENUM_ADDR]] : $*MaybeAddressOnlyPair, #MaybeAddressOnlyPair.Left!enumelt
|
|
case .Left(_):
|
|
// CHECK: [[FUNC:%.*]] = function_ref @$s6switch1byyF
|
|
// CHECK-NEXT: apply [[FUNC]](
|
|
// CHECK: destroy_addr [[P]]
|
|
// CHECK: br [[CONT]]
|
|
b()
|
|
|
|
// CHECK: [[IS_RIGHT]]:
|
|
// CHECK: [[STR_ADDR:%.*]] = unchecked_inplace_enum_data_addr [[ENUM_ADDR]] : $*MaybeAddressOnlyPair, #MaybeAddressOnlyPair.Right!enumelt
|
|
// CHECK: [[STR:%.*]] = load [take] [[STR_ADDR]]
|
|
case .Right(_):
|
|
// CHECK: [[FUNC:%.*]] = function_ref @$s6switch1cyyF
|
|
// CHECK: apply [[FUNC]](
|
|
// CHECK: destroy_value [[STR]] : $String
|
|
// CHECK: br [[CONT]]
|
|
c()
|
|
|
|
// CHECK: [[IS_BOTH]]:
|
|
// CHECK: [[P_STR_TUPLE:%.*]] = unchecked_inplace_enum_data_addr [[ENUM_ADDR]] : $*MaybeAddressOnlyPair, #MaybeAddressOnlyPair.Both!enumelt
|
|
case .Both(_):
|
|
// CHECK: [[FUNC:%.*]] = function_ref @$s6switch1dyyF
|
|
// CHECK-NEXT: apply [[FUNC]](
|
|
// CHECK: destroy_addr [[P_STR_TUPLE]]
|
|
// CHECK: br [[CONT]]
|
|
d()
|
|
}
|
|
// CHECK: [[CONT]]:
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
e()
|
|
}
|
|
|
|
enum Generic<T, U> {
|
|
case Foo(T)
|
|
case Bar(U)
|
|
}
|
|
|
|
// Check that switching over a generic instance generates verified SIL.
|
|
func test_union_generic_instance(u: Generic<Int, String>) {
|
|
switch u {
|
|
case .Foo(var x):
|
|
a()
|
|
case .Bar(var y):
|
|
b()
|
|
}
|
|
c()
|
|
}
|
|
|
|
enum Foo { case A, B }
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch05test_A11_two_unions1x1yyAA3FooO_AFtF
|
|
func test_switch_two_unions(x: Foo, y: Foo) {
|
|
// CHECK: [[T0:%.*]] = tuple (%0 : $Foo, %1 : $Foo)
|
|
// CHECK: ([[X:%.*]], [[Y:%.*]]) = destructure_tuple [[T0]]
|
|
// CHECK: switch_enum [[Y]] : $Foo, case #Foo.A!enumelt: [[IS_CASE1:bb[0-9]+]], default [[IS_NOT_CASE1:bb[0-9]+]]
|
|
|
|
switch (x, y) {
|
|
// CHECK: [[IS_CASE1]]:
|
|
case (_, Foo.A):
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
a()
|
|
|
|
// CHECK: [[IS_NOT_CASE1]]:
|
|
// CHECK: switch_enum [[X]] : $Foo, case #Foo.B!enumelt: [[IS_CASE2:bb[0-9]+]], default [[IS_NOT_CASE2:bb[0-9]+]]
|
|
// CHECK: [[IS_CASE2]]:
|
|
case (Foo.B, _):
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
b()
|
|
|
|
// CHECK: [[IS_NOT_CASE2]]:
|
|
// CHECK: switch_enum [[Y]] : $Foo, case #Foo.B!enumelt: [[IS_CASE3:bb[0-9]+]], default [[UNREACHABLE:bb[0-9]+]]
|
|
// CHECK: [[IS_CASE3]]:
|
|
case (_, Foo.B):
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
c()
|
|
|
|
// CHECK: [[UNREACHABLE]]:
|
|
// CHECK: unreachable
|
|
}
|
|
}
|
|
|
|
|
|
// <rdar://problem/14826416>
|
|
func rdar14826416<T, U>(t: T, u: U) {
|
|
switch t {
|
|
case is Int: markUsed("Int")
|
|
case is U: markUsed("U")
|
|
case _: markUsed("other")
|
|
}
|
|
}
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch12rdar14826416{{[_0-9a-zA-Z]*}}F
|
|
// CHECK: checked_cast_addr_br copy_on_success T in {{%.*}} : $*T to Int in {{%.*}} : $*Int, [[IS_INT:bb[0-9]+]], [[ISNT_INT:bb[0-9]+]]
|
|
// CHECK: [[ISNT_INT]]:
|
|
// CHECK: checked_cast_addr_br copy_on_success T in {{%.*}} : $*T to U in {{%.*}} : $*U, [[ISNT_INT_IS_U:bb[0-9]+]], [[ISNT_INT_ISNT_U:bb[0-9]+]]
|
|
|
|
// <rdar://problem/14835992>
|
|
class Rdar14835992 {}
|
|
class SubRdar14835992 : Rdar14835992 {}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch12rdar14835992{{[_0-9a-zA-Z]*}}F
|
|
func rdar14835992<T, U>(t: Rdar14835992, tt: T, uu: U) {
|
|
switch t {
|
|
case is SubRdar14835992: markUsed("Sub")
|
|
case is T: markUsed("T")
|
|
case is U: markUsed("U")
|
|
case _: markUsed("other")
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// <rdar://problem/17272985>
|
|
enum ABC { case A, B, C }
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch18testTupleWildcardsyyAA3ABCO_ADtF
|
|
// CHECK: ([[X:%.*]], [[Y:%.*]]) = destructure_tuple {{%.*}} : $(ABC, ABC)
|
|
// CHECK: switch_enum [[X]] : $ABC, case #ABC.A!enumelt: [[X_A:bb[0-9]+]], default [[X_NOT_A:bb[0-9]+]]
|
|
// CHECK: [[X_A]]:
|
|
// CHECK: function_ref @$s6switch1ayyF
|
|
// CHECK: [[X_NOT_A]](
|
|
// CHECK: switch_enum [[Y]] : $ABC, case #ABC.A!enumelt: [[Y_A:bb[0-9]+]], case #ABC.B!enumelt: [[Y_B:bb[0-9]+]], case #ABC.C!enumelt: [[Y_C:bb[0-9]+]]
|
|
// CHECK-NOT: default
|
|
// CHECK: [[Y_A]]:
|
|
// CHECK: function_ref @$s6switch1byyF
|
|
// CHECK: [[Y_B]]:
|
|
// CHECK: function_ref @$s6switch1cyyF
|
|
// CHECK: [[Y_C]]:
|
|
// CHECK: switch_enum [[X]] : $ABC, case #ABC.C!enumelt: [[X_C:bb[0-9]+]], default [[X_NOT_C:bb[0-9]+]]
|
|
// CHECK: [[X_C]]:
|
|
// CHECK: function_ref @$s6switch1dyyF
|
|
// CHECK: [[X_NOT_C]](
|
|
// CHECK: function_ref @$s6switch1eyyF
|
|
func testTupleWildcards(_ x: ABC, _ y: ABC) {
|
|
switch (x, y) {
|
|
case (.A, _):
|
|
a()
|
|
case (_, .A):
|
|
b()
|
|
case (_, .B):
|
|
c()
|
|
case (.C, .C):
|
|
d()
|
|
default:
|
|
e()
|
|
}
|
|
}
|
|
|
|
enum LabeledScalarPayload {
|
|
case Payload(name: Int)
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch24testLabeledScalarPayloadyypAA0cdE0OF
|
|
func testLabeledScalarPayload(_ lsp: LabeledScalarPayload) -> Any {
|
|
// CHECK: switch_enum {{%.*}}, case #LabeledScalarPayload.Payload!enumelt: bb1
|
|
switch lsp {
|
|
// CHECK: bb1([[TUPLE:%.*]] : $(name: Int)):
|
|
// CHECK: [[X:%.*]] = destructure_tuple [[TUPLE]]
|
|
// CHECK: [[MV:%.*]] = move_value [var_decl] [[X]] : $Int
|
|
// CHECK: [[ANY_X_ADDR:%.*]] = init_existential_addr {{%.*}}, $Int
|
|
// CHECK: store [[MV]] to [trivial] [[ANY_X_ADDR]]
|
|
case let .Payload(x):
|
|
return x
|
|
}
|
|
}
|
|
|
|
// There should be no unreachable generated.
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch19testOptionalPatternyySiSgF
|
|
func testOptionalPattern(_ value : Int?) {
|
|
// CHECK: switch_enum %0 : $Optional<Int>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: [[NILBB:bb[0-9]+]]
|
|
switch value {
|
|
case 1?: a()
|
|
case 2?: b()
|
|
case nil: d()
|
|
default: e()
|
|
}
|
|
}
|
|
|
|
|
|
// x? and .none should both be considered "similar" and thus handled in the same
|
|
// switch on the enum kind. There should be no unreachable generated.
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch19testOptionalEnumMixyS2iSgF
|
|
func testOptionalEnumMix(_ a : Int?) -> Int {
|
|
// CHECK: debug_value %0 : $Optional<Int>, let, name "a"
|
|
// CHECK-NEXT: switch_enum %0 : $Optional<Int>, case #Optional.some!enumelt: [[SOMEBB:bb[0-9]+]], case #Optional.none!enumelt: [[NILBB:bb[0-9]+]]
|
|
switch a {
|
|
case let x?:
|
|
return 0
|
|
|
|
// CHECK: [[SOMEBB]]([[X:%.*]] : $Int):
|
|
// CHECK-NEXT: [[MV:%.*]] = move_value [var_decl] [[X]] : $Int
|
|
// CHECK-NEXT: debug_value [[MV]] : $Int, let, name "x"
|
|
// CHECK: integer_literal $Builtin.IntLiteral, 0
|
|
|
|
case .none:
|
|
return 42
|
|
|
|
// CHECK: [[NILBB]]:
|
|
// CHECK: integer_literal $Builtin.IntLiteral, 42
|
|
}
|
|
}
|
|
|
|
// x? and nil should both be considered "similar" and thus handled in the same
|
|
// switch on the enum kind. There should be no unreachable generated.
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch26testOptionalEnumMixWithNilyS2iSgF
|
|
func testOptionalEnumMixWithNil(_ a : Int?) -> Int {
|
|
// CHECK: debug_value %0 : $Optional<Int>, let, name "a"
|
|
// CHECK-NEXT: switch_enum %0 : $Optional<Int>, case #Optional.some!enumelt: [[SOMEBB:bb[0-9]+]], case #Optional.none!enumelt: [[NILBB:bb[0-9]+]]
|
|
switch a {
|
|
case let x?:
|
|
return 0
|
|
|
|
// CHECK: [[SOMEBB]]([[X:%.*]] : $Int):
|
|
// CHECK-NEXT: [[MV:%.*]] = move_value [var_decl] [[X]] : $Int
|
|
// CHECK-NEXT: debug_value [[MV]] : $Int, let, name "x"
|
|
// CHECK: integer_literal $Builtin.IntLiteral, 0
|
|
|
|
case nil:
|
|
return 42
|
|
|
|
// CHECK: [[NILBB]]:
|
|
// CHECK: integer_literal $Builtin.IntLiteral, 42
|
|
}
|
|
}
|
|
|
|
// https://github.com/apple/swift/issues/46106
|
|
//
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch43testMultiPatternsWithOuterScopeSameNamedVar4base6filterySiSg_AEtF
|
|
func testMultiPatternsWithOuterScopeSameNamedVar(base: Int?, filter: Int?) {
|
|
switch(base, filter) {
|
|
|
|
case (.some(let base), .some(let filter)):
|
|
// CHECK: bb1([[BASE:%[0-9]+]] : $Int):
|
|
// CHECK: switch_enum
|
|
// CHECK: bb2([[FILTER:%[0-9]+]] : $Int):
|
|
// CHECK-NEXT: [[MV_BASE:%.*]] = move_value [var_decl] [[BASE]]
|
|
// CHECK-NEXT: [[MV_FILTER:%.*]] = move_value [var_decl] [[FILTER]]
|
|
// CHECK-NEXT: debug_value [[MV_BASE]] : $Int, let, name "base"
|
|
// CHECK-NEXT: debug_value [[MV_FILTER]] : $Int, let, name "filter"
|
|
print("both: \(base), \(filter)")
|
|
case (.some(let base), .none), (.none, .some(let base)):
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: [[MV_BASE_PATTERN:%.*]] = move_value [var_decl] [[BASE]] : $Int
|
|
// CHECK-NEXT: extend_lifetime [[MV_BASE_PATTERN]] : $Int
|
|
// CHECK-NEXT: br bb6([[MV_BASE_PATTERN]] : $Int)
|
|
|
|
// CHECK: bb5([[OTHER_BASE:%.*]] : $Int)
|
|
// CHECK-NEXT: [[MV_OTHER_BASE:%.*]] = move_value [var_decl] [[OTHER_BASE]] : $Int
|
|
// CHECK-NEXT: extend_lifetime [[MV_OTHER_BASE]] : $Int
|
|
// CHECK-NEXT: br bb6([[MV_OTHER_BASE]] : $Int)
|
|
|
|
// CHECK: bb6([[ARG:%.*]] : $Int):
|
|
// CHECK-NEXT: debug_value [[ARG]] : $Int, let, name "base"
|
|
print("single: \(base)")
|
|
default:
|
|
print("default")
|
|
}
|
|
}
|
|
|
|
// All cases are unreachable, either structurally (tuples involving Never) or
|
|
// nominally (empty enums). We fold all of these to 'unreachable'.
|
|
enum MyNever {}
|
|
func ~= (_ : MyNever, _ : MyNever) -> Bool { return true }
|
|
func myFatalError() -> MyNever { fatalError("asdf") }
|
|
|
|
func testUninhabitedSwitchScrutinee() {
|
|
func test1(x : MyNever) {
|
|
// CHECK: bb0(%0 : $MyNever):
|
|
// CHECK-NEXT: debug_value %0 : $MyNever, let, name "x"
|
|
// CHECK-NEXT: unreachable
|
|
switch x {
|
|
case myFatalError(): break
|
|
case myFatalError(): break
|
|
case myFatalError(): break
|
|
}
|
|
}
|
|
func test2(x : Never) {
|
|
// CHECK: bb0(%0 : $Never):
|
|
// CHECK-NEXT: debug_value %0 : $Never, let, name "x"
|
|
// CHECK-NEXT: unreachable
|
|
switch (x, x) {}
|
|
}
|
|
func test3(x : Never) {
|
|
// CHECK: unreachable
|
|
// CHECK-NEXT: } // end sil function '$s6switch30testUninhabitedSwitchScrutineeyyF5test3L_1xys5NeverO_tF'
|
|
switch (x, 5, x) {}
|
|
}
|
|
func test4(x : Never) {
|
|
// CHECK: unreachable
|
|
// CHECK-NEXT: } // end sil function '$s6switch30testUninhabitedSwitchScrutineeyyF5test4L_1xys5NeverO_tF'
|
|
switch ((8, 6, 7), (5, 3, (0, x))) {}
|
|
}
|
|
func test5() {
|
|
// CHECK: %0 = function_ref @$s6switch12myFatalErrorAA7MyNeverOyF : $@convention(thin) () -> MyNever
|
|
// CHECK-NEXT: %1 = apply %0() : $@convention(thin) () -> MyNever
|
|
// CHECK-NEXT: ignored_use %1
|
|
// CHECK-NEXT: unreachable
|
|
switch myFatalError() {}
|
|
}
|
|
}
|
|
|
|
// Make sure that we properly can handle address only tuples with loadable
|
|
// subtypes.
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch33address_only_with_trivial_subtypeyyAA21TrivialSingleCaseEnumO_yptF : $@convention(thin) (TrivialSingleCaseEnum, @in_guaranteed Any) -> () {
|
|
// CHECK: [[MEM:%.*]] = alloc_stack $(TrivialSingleCaseEnum, Any)
|
|
// CHECK: [[INIT_TUP_0:%.*]] = tuple_element_addr [[MEM]] : $*(TrivialSingleCaseEnum, Any), 0
|
|
// CHECK: [[INIT_TUP_1:%.*]] = tuple_element_addr [[MEM]] : $*(TrivialSingleCaseEnum, Any), 1
|
|
// CHECK: store {{%.*}} to [trivial] [[INIT_TUP_0]]
|
|
// CHECK: copy_addr [take] {{%.*}} to [init] [[INIT_TUP_1]]
|
|
// CHECK: [[TUP_0:%.*]] = tuple_element_addr [[MEM]] : $*(TrivialSingleCaseEnum, Any), 0
|
|
// CHECK: [[TUP_0_VAL:%.*]] = load [trivial] [[TUP_0]]
|
|
// CHECK: [[TUP_1:%.*]] = tuple_element_addr [[MEM]] : $*(TrivialSingleCaseEnum, Any), 1
|
|
// CHECK: switch_enum [[TUP_0_VAL]]
|
|
//
|
|
// CHECK: } // end sil function '$s6switch33address_only_with_trivial_subtypeyyAA21TrivialSingleCaseEnumO_yptF'
|
|
func address_only_with_trivial_subtype(_ a: TrivialSingleCaseEnum, _ value: Any) {
|
|
switch (a, value) {
|
|
case (.a, _):
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch36address_only_with_nontrivial_subtypeyyAA24NonTrivialSingleCaseEnumO_yptF : $@convention(thin) (@guaranteed NonTrivialSingleCaseEnum, @in_guaranteed Any) -> () {
|
|
// CHECK: [[MEM:%.*]] = alloc_stack $(NonTrivialSingleCaseEnum, Any)
|
|
// CHECK: [[INIT_TUP_0:%.*]] = tuple_element_addr [[MEM]] : $*(NonTrivialSingleCaseEnum, Any), 0
|
|
// CHECK: [[INIT_TUP_1:%.*]] = tuple_element_addr [[MEM]] : $*(NonTrivialSingleCaseEnum, Any), 1
|
|
// CHECK: store {{%.*}} to [init] [[INIT_TUP_0]]
|
|
// CHECK: copy_addr [take] {{%.*}} to [init] [[INIT_TUP_1]]
|
|
// CHECK: [[TUP_0:%.*]] = tuple_element_addr [[MEM]] : $*(NonTrivialSingleCaseEnum, Any), 0
|
|
// CHECK: [[TUP_0_VAL:%.*]] = load_borrow [[TUP_0]]
|
|
// CHECK: [[TUP_1:%.*]] = tuple_element_addr [[MEM]] : $*(NonTrivialSingleCaseEnum, Any), 1
|
|
// CHECK: switch_enum [[TUP_0_VAL]]
|
|
//
|
|
// CHECK: bb1([[CASE_VAL:%.*]] :
|
|
// CHECK-NEXT: destroy_addr [[TUP_1]]
|
|
// CHECK-NEXT: end_borrow [[TUP_0_VAL]]
|
|
// CHECK-NEXT: destroy_addr [[TUP_0]]
|
|
// CHECK-NEXT: dealloc_stack [[MEM]]
|
|
//
|
|
// CHECK: bb2:
|
|
// CHECK-NEXT: destroy_addr [[MEM]]
|
|
// CHECK-NEXT: dealloc_stack [[MEM]]
|
|
// CHECK: } // end sil function '$s6switch36address_only_with_nontrivial_subtypeyyAA24NonTrivialSingleCaseEnumO_yptF'
|
|
func address_only_with_nontrivial_subtype(_ a: NonTrivialSingleCaseEnum, _ value: Any) {
|
|
switch (a, value) {
|
|
case (.a, _):
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// This test makes sure that when we have a tuple that is partly address only
|
|
// and partially an object that even though we access the object at +0 via a
|
|
// load_borrow, we do not lose the +1 from the original tuple formation.
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch35partial_address_only_tuple_dispatchyyAA5KlassC_ypSgtF : $@convention(thin) (@guaranteed Klass, @in_guaranteed Optional<Any>) -> () {
|
|
// CHECK: bb0([[ARG0:%.*]] : @guaranteed $Klass, [[ARG1:%.*]] : $*Optional<Any>):
|
|
// CHECK: [[ARG0_COPY:%.*]] = copy_value [[ARG0]]
|
|
// CHECK: [[ARG1_COPY:%.*]] = alloc_stack $Optional<Any>
|
|
// CHECK: copy_addr [[ARG1]] to [init] [[ARG1_COPY]]
|
|
// CHECK: [[TUP:%.*]] = alloc_stack $(Klass, Optional<Any>)
|
|
// CHECK: [[TUP_0:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 0
|
|
// CHECK: [[TUP_1:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 1
|
|
// CHECK: store [[ARG0_COPY]] to [init] [[TUP_0]]
|
|
// CHECK: copy_addr [take] [[ARG1_COPY]] to [init] [[TUP_1]]
|
|
// CHECK: [[TUP_0:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 0
|
|
// CHECK: [[TUP_0_VAL:%.*]] = load_borrow [[TUP_0]]
|
|
// CHECK: [[TUP_1:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 1
|
|
// CHECK: destroy_addr [[TUP_1]]
|
|
// CHECK: end_borrow [[TUP_0_VAL]]
|
|
// CHECK: destroy_addr [[TUP_0]]
|
|
// CHECK: dealloc_stack [[TUP]]
|
|
// CHECK: br bb2
|
|
//
|
|
// CHECK: bb1:
|
|
// CHECK: destroy_addr [[TUP]]
|
|
// CHECK: dealloc_stack [[TUP]]
|
|
// CHECK: } // end sil function '$s6switch35partial_address_only_tuple_dispatchyyAA5KlassC_ypSgtF'
|
|
func partial_address_only_tuple_dispatch(_ name: Klass, _ value: Any?) {
|
|
switch (name, value) {
|
|
case (_, _):
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch50partial_address_only_tuple_dispatch_with_fail_caseyyAA5KlassC_ypSgtF : $@convention(thin) (@guaranteed Klass, @in_guaranteed Optional<Any>) -> () {
|
|
// CHECK: bb0([[ARG0:%.*]] : @guaranteed $Klass, [[ARG1:%.*]] : $*Optional<Any>):
|
|
// CHECK: [[ARG0_COPY:%.*]] = copy_value [[ARG0]]
|
|
// CHECK: [[ARG1_COPY:%.*]] = alloc_stack $Optional<Any>
|
|
// CHECK: copy_addr [[ARG1]] to [init] [[ARG1_COPY]]
|
|
// CHECK: [[TUP:%.*]] = alloc_stack $(Klass, Optional<Any>)
|
|
// CHECK: [[TUP_0:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 0
|
|
// CHECK: [[TUP_1:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 1
|
|
// CHECK: store [[ARG0_COPY]] to [init] [[TUP_0]]
|
|
// CHECK: copy_addr [take] [[ARG1_COPY]] to [init] [[TUP_1]]
|
|
// CHECK: [[TUP_0:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 0
|
|
// CHECK: [[TUP_0_VAL:%.*]] = load_borrow [[TUP_0]]
|
|
// CHECK: [[TUP_1:%.*]] = tuple_element_addr [[TUP]] : $*(Klass, Optional<Any>), 1
|
|
// CHECK: checked_cast_br Klass in [[TUP_0_VAL]] : $Klass to AnyObject, [[IS_ANYOBJECT_BB:bb[0-9]+]], [[ISNOT_ANYOBJECT_BB:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[IS_ANYOBJECT_BB]]([[ANYOBJECT:%.*]] : @guaranteed $AnyObject):
|
|
// CHECK: [[ANYOBJECT_COPY:%.*]] = copy_value [[ANYOBJECT]]
|
|
// ... CASE BODY ...
|
|
// CHECK: destroy_addr [[TUP_1]]
|
|
// CHECK: end_borrow [[TUP_0_VAL]]
|
|
// CHECK: destroy_addr [[TUP_0]]
|
|
// CHECK: dealloc_stack [[TUP]]
|
|
// CHECK: br [[EXIT_BB:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[ISNOT_ANYOBJECT_BB]](
|
|
// CHECK: switch_enum_addr [[TUP_1]] : $*Optional<Any>, case #Optional.some!enumelt: [[HAS_TUP_1_BB:bb[0-9]+]], default [[NO_TUP_1_BB:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[HAS_TUP_1_BB]]:
|
|
// CHECK-NEXT: [[OPT_ANY_ADDR:%.*]] = alloc_stack $Optional<Any>
|
|
// CHECK-NEXT: copy_addr [[TUP_1]] to [init] [[OPT_ANY_ADDR]]
|
|
// CHECK-NEXT: [[SOME_ANY_ADDR:%.*]] = unchecked_inplace_enum_data_addr [[OPT_ANY_ADDR]]
|
|
// CHECK-NEXT: [[ANYOBJECT_ADDR:%.*]] = alloc_stack $AnyObject
|
|
// CHECK-NEXT: checked_cast_addr_br copy_on_success Any in {{%.*}} : $*Any to AnyObject in {{%.*}} : $*AnyObject, [[IS_ANY_BB:bb[0-9]+]], [[ISNOT_ANY_BB:bb[0-9]+]]
|
|
//
|
|
// Make sure that we clean up everything here. We are exiting here.
|
|
//
|
|
// CHECK: [[IS_ANY_BB]]:
|
|
// CHECK-NEXT: [[ANYOBJECT:%.*]] = load [take] [[ANYOBJECT_ADDR]]
|
|
// CHECK-NEXT: [[MOVED:%.*]] = move_value
|
|
// CHECK-NEXT: debug_value
|
|
// CHECK-NEXT: destroy_value [[MOVED]]
|
|
// CHECK-NEXT: dealloc_stack [[ANYOBJECT_ADDR]]
|
|
// CHECK-NEXT: destroy_addr [[SOME_ANY_ADDR]]
|
|
// CHECK-NEXT: dealloc_stack [[OPT_ANY_ADDR]]
|
|
// CHECK-NEXT: destroy_addr [[TUP_1]]
|
|
// CHECK-NEXT: end_borrow [[TUP_0_VAL]]
|
|
// CHECK-NEXT: destroy_addr [[TUP_0]]
|
|
// CHECK-NEXT: dealloc_stack [[TUP]]
|
|
// CHECK-NEXT: dealloc_stack
|
|
// CHECK-NEXT: br [[EXIT_BB]]
|
|
//
|
|
// CHECK: [[ISNOT_ANY_BB]]:
|
|
// CHECK-NEXT: dealloc_stack [[ANYOBJECT_ADDR]]
|
|
// CHECK-NEXT: destroy_addr [[SOME_ANY_ADDR]]
|
|
// CHECK-NEXT: dealloc_stack [[OPT_ANY_ADDR]]
|
|
// CHECK-NEXT: end_borrow
|
|
// CHECK-NEXT: br [[UNFORWARD_BB:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[NO_TUP_1_BB]]:
|
|
// CHECK-NEXT: end_borrow
|
|
// CHECK-NEXT: br [[UNFORWARD_BB]]
|
|
//
|
|
// CHECK: [[UNFORWARD_BB]]:
|
|
// CHECK-NEXT: destroy_addr [[TUP]]
|
|
// CHECK-NEXT: dealloc_stack [[TUP]]
|
|
// CHECK: br [[EXIT_BB]]
|
|
//
|
|
// CHECK: [[EXIT_BB]]:
|
|
// ...
|
|
// CHECK: } // end sil function '$s6switch50partial_address_only_tuple_dispatch_with_fail_caseyyAA5KlassC_ypSgtF'
|
|
func partial_address_only_tuple_dispatch_with_fail_case(_ name: Klass, _ value: Any?) {
|
|
switch (name, value) {
|
|
case let (x as AnyObject, _):
|
|
break
|
|
case let (_, y as AnyObject):
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// This was crashing the ownership verifier at some point and was reported in
|
|
// https://github.com/apple/swift/issues/49213. Just make sure that we still
|
|
// pass the ownership verifier.
|
|
//
|
|
// `indirect` is necessary; generic parameter is necessary.
|
|
indirect enum IndirectGenericEnum<Element> {
|
|
// Tuple associated value is necessary; one element must be a function,
|
|
// the other must be a non-function using the generic parameter.
|
|
// (The original associated value was `(where: (Element) -> Bool, of: Element?)`,
|
|
// to give you an idea of the variety of types.)
|
|
case index((Int) -> Void, Element)
|
|
|
|
// Function can be in an extension or not. Can have a return value or not. Can
|
|
// have a parameter or not. Can be generic or not.
|
|
func relative() {
|
|
switch self {
|
|
// Matching the case is necessary. You can capture or ignore the associated
|
|
// values.
|
|
case .index:
|
|
// Body doesn't matter.
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure that we properly create switch_enum success arguments if we have an
|
|
// associated type that is a void type.
|
|
func testVoidType() {
|
|
let x: Optional<()> = ()
|
|
switch x {
|
|
case .some(let x):
|
|
break
|
|
case .none:
|
|
break
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////
|
|
// Fallthrough Multiple Case Label Item Tests //
|
|
////////////////////////////////////////////////
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch28addressOnlyFallthroughCalleeyyAA015MultipleAddressC8CaseEnumOyxGSzRzlF : $@convention(thin) <T where T : BinaryInteger> (@in_guaranteed MultipleAddressOnlyCaseEnum<T>) -> () {
|
|
// CHECK: bb0([[ARG:%.*]] :
|
|
// CHECK: [[AB_PHI:%.*]] = alloc_stack $T
|
|
// CHECK: [[ABB_PHI:%.*]] = alloc_stack $T
|
|
// CHECK: [[ABBC_PHI:%.*]] = alloc_stack $T
|
|
// CHECK: [[SWITCH_ENUM_ARG:%.*]] = alloc_stack $MultipleAddressOnlyCaseEnum<T>
|
|
// CHECK: copy_addr [[ARG]] to [init] [[SWITCH_ENUM_ARG]]
|
|
// CHECK: switch_enum_addr [[SWITCH_ENUM_ARG]] : $*MultipleAddressOnlyCaseEnum<T>, case #MultipleAddressOnlyCaseEnum.a!enumelt: [[BB_A:bb[0-9]+]], case #MultipleAddressOnlyCaseEnum.b!enumelt: [[BB_B:bb[0-9]+]], case #MultipleAddressOnlyCaseEnum.c!enumelt: [[BB_C:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[BB_A]]:
|
|
// CHECK: [[SWITCH_ENUM_ARG_PROJ:%.*]] = unchecked_inplace_enum_data_addr [[SWITCH_ENUM_ARG]]
|
|
// CHECK: [[CASE_BODY_VAR_A:%.*]] = alloc_stack [lexical] [var_decl] $T, let, name "x"
|
|
// CHECK: copy_addr [take] [[SWITCH_ENUM_ARG_PROJ]] to [init] [[CASE_BODY_VAR_A]]
|
|
// CHECK: copy_addr [[CASE_BODY_VAR_A]] to [init] [[AB_PHI]]
|
|
// CHECK: destroy_addr [[CASE_BODY_VAR_A]]
|
|
// CHECK: br [[BB_AB:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[BB_B]]:
|
|
// CHECK: [[SWITCH_ENUM_ARG_PROJ:%.*]] = unchecked_inplace_enum_data_addr [[SWITCH_ENUM_ARG]]
|
|
// CHECK: [[CASE_BODY_VAR_B:%.*]] = alloc_stack [lexical] [var_decl] $T, let, name "x"
|
|
// CHECK: copy_addr [[SWITCH_ENUM_ARG_PROJ]] to [init] [[CASE_BODY_VAR_B]]
|
|
// CHECK: [[FUNC_CMP:%.*]] = function_ref @$sSzsE2eeoiySbx_qd__tSzRd__lFZ :
|
|
// CHECK: [[GUARD_RESULT:%.*]] = apply [[FUNC_CMP]]<T, Int>([[CASE_BODY_VAR_B]], {{%.*}}, {{%.*}})
|
|
// CHECK: [[GUARD_RESULT_EXT:%.*]] = struct_extract [[GUARD_RESULT]]
|
|
// CHECK: cond_br [[GUARD_RESULT_EXT]], [[BB_B_GUARD_SUCC:bb[0-9]+]], [[BB_B_GUARD_FAIL:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[BB_B_GUARD_SUCC]]:
|
|
// CHECK: copy_addr [[CASE_BODY_VAR_B]] to [init] [[AB_PHI]]
|
|
// CHECK: destroy_addr [[CASE_BODY_VAR_B]]
|
|
// CHECK: destroy_addr [[SWITCH_ENUM_ARG_PROJ]]
|
|
// CHECK: br [[BB_AB]]
|
|
//
|
|
// CHECK: [[BB_AB]]:
|
|
// CHECK: copy_addr [[AB_PHI]] to [init] [[ABB_PHI]]
|
|
// CHECK: destroy_addr [[AB_PHI]]
|
|
// CHECK: br [[BB_AB_CONT:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[BB_AB_CONT]]:
|
|
// CHECK: copy_addr [[ABB_PHI]] to [init] [[ABBC_PHI]]
|
|
// CHECK: destroy_addr [[ABB_PHI]]
|
|
// CHECK: br [[BB_FINAL_CONT:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[BB_B_GUARD_FAIL]]:
|
|
// CHECK: destroy_addr [[CASE_BODY_VAR_B]]
|
|
// CHECK: [[CASE_BODY_VAR_B_2:%.*]] = alloc_stack [lexical] [var_decl] $T, let, name "x"
|
|
// CHECK: copy_addr [take] [[SWITCH_ENUM_ARG_PROJ]] to [init] [[CASE_BODY_VAR_B_2]]
|
|
// CHECK: copy_addr [[CASE_BODY_VAR_B_2]] to [init] [[ABB_PHI]]
|
|
// CHECK: br [[BB_AB_CONT]]
|
|
//
|
|
// CHECK: [[BB_C]]:
|
|
// CHECK: [[SWITCH_ENUM_ARG_PROJ:%.*]] = unchecked_inplace_enum_data_addr [[SWITCH_ENUM_ARG]]
|
|
// CHECK: [[CASE_BODY_VAR_C:%.*]] = alloc_stack [lexical] [var_decl] $T, let, name "x"
|
|
// CHECK: copy_addr [take] [[SWITCH_ENUM_ARG_PROJ]] to [init] [[CASE_BODY_VAR_C]]
|
|
// CHECK: copy_addr [[CASE_BODY_VAR_C]] to [init] [[ABBC_PHI]]
|
|
// CHECK: destroy_addr [[CASE_BODY_VAR_C]]
|
|
// CHECK: br [[BB_FINAL_CONT]]
|
|
//
|
|
// CHECK: [[BB_FINAL_CONT]]:
|
|
// CHECK: destroy_addr [[ABBC_PHI]]
|
|
// CHECK: return
|
|
// CHECK: } // end sil function '$s6switch28addressOnlyFallthroughCalleeyyAA015MultipleAddressC8CaseEnumOyxGSzRzlF'
|
|
func addressOnlyFallthroughCallee<T : BinaryInteger>(_ e : MultipleAddressOnlyCaseEnum<T>) {
|
|
switch e {
|
|
case .a(let x): fallthrough
|
|
case .b(let x) where x == 2: fallthrough
|
|
case .b(let x): fallthrough
|
|
case .c(let x):
|
|
print(x)
|
|
}
|
|
}
|
|
|
|
func addressOnlyFallthroughCaller() {
|
|
var myFoo : MultipleAddressOnlyCaseEnum = MultipleAddressOnlyCaseEnum.a(10)
|
|
addressOnlyFallthroughCallee(myFoo)
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s6switch35nonTrivialLoadableFallthroughCalleeyyAA011MultipleNonC8CaseEnumOF : $@convention(thin) (@guaranteed MultipleNonTrivialCaseEnum) -> () {
|
|
// CHECK: bb0([[ARG:%.*]] : @guaranteed $MultipleNonTrivialCaseEnum):
|
|
// CHECK: switch_enum [[ARG]] : $MultipleNonTrivialCaseEnum, case #MultipleNonTrivialCaseEnum.a!enumelt: [[BB_A:bb[0-9]+]], case #MultipleNonTrivialCaseEnum.b!enumelt: [[BB_B:bb[0-9]+]], case #MultipleNonTrivialCaseEnum.c!enumelt: [[BB_C:bb[0-9]+]]
|
|
//
|
|
// CHECK: [[BB_A]]([[BB_A_ARG:%.*]] : @guaranteed
|
|
// CHECK: [[BB_A_ARG_COPY:%.*]] = copy_value [[BB_A_ARG]]
|
|
// CHECK: [[BB_A_ARG_COPY_MOVE:%.*]] = move_value [lexical] [var_decl] [[BB_A_ARG_COPY]]
|
|
// CHECK: [[BB_A_ARG_COPY_BORROW:%.*]] = begin_borrow [[BB_A_ARG_COPY_MOVE]]
|
|
// CHECK: apply {{%.*}}([[BB_A_ARG_COPY_BORROW]])
|
|
// CHECK: [[RESULT:%.*]] = copy_value [[BB_A_ARG_COPY_MOVE]]
|
|
// CHECK: br [[BB_AB:bb[0-9]+]]([[RESULT]] :
|
|
//
|
|
// CHECK: [[BB_B]]([[BB_B_ARG:%.*]] : @guaranteed
|
|
// CHECK: [[BB_B_ARG_COPY:%.*]] = copy_value [[BB_B_ARG]]
|
|
// CHECK: [[BB_B_ARG_COPY_MOVE:%.*]] = move_value [lexical] [var_decl] [[BB_B_ARG_COPY]]
|
|
// CHECK: [[RESULT:%.*]] = copy_value [[BB_B_ARG_COPY_MOVE]]
|
|
// CHECK: br [[BB_AB:bb[0-9]+]]([[RESULT]] :
|
|
//
|
|
// CHECK: [[BB_AB:bb[0-9]+]]([[BB_AB_PHI:%.*]] : @owned
|
|
// CHECK: [[BB_AB_PHI_BORROW:%.*]] = begin_borrow [[BB_AB_PHI]]
|
|
// CHECK: apply {{%.*}}([[BB_AB_PHI_BORROW]])
|
|
// CHECK: [[RESULT:%.*]] = copy_value [[BB_AB_PHI]]
|
|
// CHECK: br [[BB_ABC:bb[0-9]+]]([[RESULT]] :
|
|
//
|
|
// CHECK: [[BB_C]]([[BB_C_ARG:%.*]] : @guaranteed
|
|
// CHECK: [[BB_C_COPY:%.*]] = copy_value [[BB_C_ARG]]
|
|
// CHECK: [[BB_C_BORROWED_MOVE:%.*]] = move_value [lexical] [var_decl] [[BB_C_COPY]]
|
|
// CHECK: [[RESULT:%.*]] = copy_value [[BB_C_BORROWED_MOVE]]
|
|
// CHECK: br [[BB_ABC]]([[RESULT]] :
|
|
//
|
|
// CHECK: [[BB_ABC]]([[BB_ABC_ARG:%.*]] : @owned
|
|
// CHECK: [[BB_ABC_ARG_BORROW:%.*]] = begin_borrow [[BB_ABC_ARG]]
|
|
// CHECK: apply {{%.*}}([[BB_ABC_ARG_BORROW]])
|
|
// CHECK: return
|
|
// CHECK: } // end sil function '$s6switch35nonTrivialLoadableFallthroughCalleeyyAA011MultipleNonC8CaseEnumOF'
|
|
func nonTrivialLoadableFallthroughCallee(_ e : MultipleNonTrivialCaseEnum) {
|
|
switch e {
|
|
case .a(let x):
|
|
a(x)
|
|
fallthrough
|
|
case .b(let x):
|
|
b(x)
|
|
fallthrough
|
|
case .c(let x):
|
|
c(x)
|
|
}
|
|
}
|
|
|
|
// Just make sure that we do not crash on this.
|
|
func nonTrivialLoadableFallthroughCalleeGuards(_ e : MultipleNonTrivialCaseEnum) {
|
|
switch e {
|
|
case .a(let x) where x.isFalse:
|
|
a(x)
|
|
fallthrough
|
|
case .a(let x) where x.isTrue:
|
|
a(x)
|
|
fallthrough
|
|
case .b(let x) where x.isTrue:
|
|
b(x)
|
|
fallthrough
|
|
case .b(let x) where x.isFalse:
|
|
b(x)
|
|
fallthrough
|
|
case .c(let x) where x.isTrue:
|
|
c(x)
|
|
fallthrough
|
|
case .c(let x) where x.isFalse:
|
|
c(x)
|
|
break
|
|
default:
|
|
d()
|
|
}
|
|
}
|
|
|
|
func nonTrivialLoadableFallthroughCallee2(_ e : MultipleNonTrivialCaseEnum) {
|
|
switch e {
|
|
case .a(let x):
|
|
a(x)
|
|
fallthrough
|
|
case .b(let x):
|
|
b(x)
|
|
break
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
// Make sure that we do not crash while emitting this code.
|
|
//
|
|
// DISCUSSION: The original crash was due to us performing an assignment/lookup
|
|
// on the VarLocs DenseMap in the same statement. This was caught be an
|
|
// asanified compiler. This test is just to make sure we do not regress.
|
|
enum Storage {
|
|
case empty
|
|
case single(Int)
|
|
case pair(Int, Int)
|
|
case array([Int])
|
|
|
|
subscript(range: [Int]) -> Storage {
|
|
get {
|
|
return .empty
|
|
}
|
|
set {
|
|
switch self {
|
|
case .empty:
|
|
break
|
|
case .single(let index):
|
|
break
|
|
case .pair(let first, let second):
|
|
switch (range[0], range[1]) {
|
|
case (0, 0):
|
|
switch newValue {
|
|
case .empty:
|
|
break
|
|
case .single(let other):
|
|
break
|
|
case .pair(let otherFirst, let otherSecond):
|
|
break
|
|
case .array(let other):
|
|
break
|
|
}
|
|
break
|
|
case (0, 1):
|
|
switch newValue {
|
|
case .empty:
|
|
break
|
|
case .single(let other):
|
|
break
|
|
case .pair(let otherFirst, let otherSecond):
|
|
break
|
|
case .array(let other):
|
|
break
|
|
}
|
|
break
|
|
case (0, 2):
|
|
break
|
|
case (1, 2):
|
|
switch newValue {
|
|
case .empty:
|
|
break
|
|
case .single(let other):
|
|
break
|
|
case .pair(let otherFirst, let otherSecond):
|
|
break
|
|
case .array(let other):
|
|
self = .array([first] + other)
|
|
}
|
|
break
|
|
case (2, 2):
|
|
switch newValue {
|
|
case .empty:
|
|
break
|
|
case .single(let other):
|
|
break
|
|
case .pair(let otherFirst, let otherSecond):
|
|
break
|
|
case .array(let other):
|
|
self = .array([first, second] + other)
|
|
}
|
|
break
|
|
default:
|
|
let r = range
|
|
}
|
|
case .array(let indexes):
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make sure that we do not leak tuple elements if we fail to match the first
|
|
// tuple element.
|
|
enum rdar49990484Enum1 {
|
|
case case1(Klass)
|
|
case case2(Klass, Int)
|
|
}
|
|
|
|
enum rdar49990484Enum2 {
|
|
case case1(Klass)
|
|
case case2(rdar49990484Enum1, Klass)
|
|
}
|
|
|
|
struct rdar49990484Struct {
|
|
var value: rdar49990484Enum2
|
|
|
|
func doSomethingIfLet() {
|
|
if case let .case2(.case2(k, _), _) = value {
|
|
return
|
|
}
|
|
}
|
|
|
|
func doSomethingSwitch() {
|
|
switch value {
|
|
case let .case2(.case2(k, _), _):
|
|
return
|
|
default:
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func doSomethingGuardLet() {
|
|
guard case let .case2(.case2(k, _), _) = value else {
|
|
return
|
|
}
|
|
}
|
|
}
|