// 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_take_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_take_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_take_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_take_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_take_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_take_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_take_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
    }
  }
}
