// RUN: %target-swift-emit-silgen -module-name indirect_enum -Xllvm -sil-print-debuginfo %s | %FileCheck %s indirect enum TreeA { case Nil case Leaf(T) case Branch(left: TreeA, right: TreeA) } // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum11TreeA_cases_1l1ryx_AA0C1AOyxGAGtlF : $@convention(thin) (@in_guaranteed T, @guaranteed TreeA, @guaranteed TreeA) -> () { func TreeA_cases(_ t: T, l: TreeA, r: TreeA) { // CHECK: bb0([[ARG1:%.*]] : $*T, [[ARG2:%.*]] : @guaranteed $TreeA, [[ARG3:%.*]] : @guaranteed $TreeA): // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeA.Type // CHECK-NEXT: [[NIL:%.*]] = enum $TreeA, #TreeA.Nil!enumelt // CHECK-NOT: destroy_value [[NIL]] let _ = TreeA.Nil // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeA.Type // CHECK-NEXT: [[T_BUF:%.*]] = alloc_stack $T // CHECK-NEXT: copy_addr [[ARG1]] to [initialization] [[T_BUF]] : $*T // CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } // CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]] // CHECK-NEXT: copy_addr [take] [[T_BUF]] to [initialization] [[PB]] // CHECK-NEXT: [[LEAF:%.*]] = enum $TreeA, #TreeA.Leaf!enumelt, [[BOX]] // CHECK-NEXT: destroy_value [[LEAF]] // CHECK-NEXT: dealloc_stack [[T_BUF]] : $*T let _ = TreeA.Leaf(t) // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeA.Type // CHECK-NEXT: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] // CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] // CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var (left: TreeA<τ_0_0>, right: TreeA<τ_0_0>) } // CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]] // CHECK-NEXT: [[LEFT:%.*]] = tuple_element_addr [[PB]] : $*(left: TreeA, right: TreeA), 0 // CHECK-NEXT: [[RIGHT:%.*]] = tuple_element_addr [[PB]] : $*(left: TreeA, right: TreeA), 1 // CHECK-NEXT: store [[ARG2_COPY]] to [init] [[LEFT]] // CHECK-NEXT: store [[ARG3_COPY]] to [init] [[RIGHT]] // CHECK-NEXT: [[BRANCH:%.*]] = enum $TreeA, #TreeA.Branch!enumelt, [[BOX]] // CHECK-NEXT: destroy_value [[BRANCH]] let _ = TreeA.Branch(left: l, right: r) } // CHECK: // end sil function '$s13indirect_enum11TreeA_cases_1l1ryx_AA0C1AOyxGAGtlF' // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum16TreeA_reabstractyyS2icF : $@convention(thin) (@guaranteed @callee_guaranteed (Int) -> Int) -> () { func TreeA_reabstract(_ f: @escaping (Int) -> Int) { // CHECK: bb0([[ARG:%.*]] : @guaranteed $@callee_guaranteed (Int) -> Int): // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeA<(Int) -> Int>.Type // CHECK-NEXT: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var τ_0_0 } <(Int) -> Int> // CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]] // CHECK: [[THUNK:%.*]] = function_ref @$sS2iIegyd_S2iIegnr_TR // CHECK-NEXT: [[FN:%.*]] = partial_apply [callee_guaranteed] [[THUNK]]([[ARG_COPY]]) // CHECK-NEXT: [[FNC:%.*]] = convert_function [[FN]] // CHECK-NEXT: store [[FNC]] to [init] [[PB]] // CHECK-NEXT: [[LEAF:%.*]] = enum $TreeA<(Int) -> Int>, #TreeA.Leaf!enumelt, [[BOX]] // CHECK-NEXT: destroy_value [[LEAF]] // CHECK: return let _ = TreeA<(Int) -> Int>.Leaf(f) } // CHECK: } // end sil function '$s13indirect_enum16TreeA_reabstractyyS2icF' enum TreeB { case Nil case Leaf(T) indirect case Branch(left: TreeB, right: TreeB) } // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum11TreeB_cases_1l1ryx_AA0C1BOyxGAGtlF func TreeB_cases(_ t: T, l: TreeB, r: TreeB) { // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeB.Type // CHECK: [[NIL:%.*]] = alloc_stack $TreeB // CHECK-NEXT: inject_enum_addr [[NIL]] : $*TreeB, #TreeB.Nil!enumelt // CHECK-NEXT: destroy_addr [[NIL]] // CHECK-NEXT: dealloc_stack [[NIL]] let _ = TreeB.Nil // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeB.Type // CHECK-NEXT: [[T_BUF:%.*]] = alloc_stack $T // CHECK-NEXT: copy_addr %0 to [initialization] [[T_BUF]] // CHECK-NEXT: [[LEAF:%.*]] = alloc_stack $TreeB // CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[LEAF]] : $*TreeB, #TreeB.Leaf!enumelt // CHECK-NEXT: copy_addr [take] [[T_BUF]] to [initialization] [[PAYLOAD]] // CHECK-NEXT: inject_enum_addr [[LEAF]] : $*TreeB, #TreeB.Leaf!enumelt // CHECK-NEXT: destroy_addr [[LEAF]] // CHECK-NEXT: dealloc_stack [[LEAF]] // CHECK-NEXT: dealloc_stack [[T_BUF]] let _ = TreeB.Leaf(t) // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeB.Type // CHECK-NEXT: [[ARG1_COPY:%.*]] = alloc_stack $TreeB // CHECK-NEXT: copy_addr %1 to [initialization] [[ARG1_COPY]] : $*TreeB // CHECK-NEXT: [[ARG2_COPY:%.*]] = alloc_stack $TreeB // CHECK-NEXT: copy_addr %2 to [initialization] [[ARG2_COPY]] : $*TreeB // CHECK-NEXT: [[BOX:%.*]] = alloc_box $<τ_0_0> { var (left: TreeB<τ_0_0>, right: TreeB<τ_0_0>) } // CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]] // CHECK-NEXT: [[LEFT:%.*]] = tuple_element_addr [[PB]] // CHECK-NEXT: [[RIGHT:%.*]] = tuple_element_addr [[PB]] // CHECK-NEXT: copy_addr [take] [[ARG1_COPY]] to [initialization] [[LEFT]] : $*TreeB // CHECK-NEXT: copy_addr [take] [[ARG2_COPY]] to [initialization] [[RIGHT]] : $*TreeB // CHECK-NEXT: [[BRANCH:%.*]] = alloc_stack $TreeB // CHECK-NEXT: [[PAYLOAD:%.*]] = init_enum_data_addr [[BRANCH]] // CHECK-NEXT: store [[BOX]] to [init] [[PAYLOAD]] // CHECK-NEXT: inject_enum_addr [[BRANCH]] : $*TreeB, #TreeB.Branch!enumelt // CHECK-NEXT: destroy_addr [[BRANCH]] // CHECK-NEXT: dealloc_stack [[BRANCH]] // CHECK-NEXT: dealloc_stack [[ARG2_COPY]] // CHECK-NEXT: dealloc_stack [[ARG1_COPY]] let _ = TreeB.Branch(left: l, right: r) // CHECK: return } // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum13TreeInt_cases_1l1rySi_AA0cD0OAFtF : $@convention(thin) (Int, @guaranteed TreeInt, @guaranteed TreeInt) -> () func TreeInt_cases(_ t: Int, l: TreeInt, r: TreeInt) { // CHECK: bb0([[ARG1:%.*]] : $Int, [[ARG2:%.*]] : @guaranteed $TreeInt, [[ARG3:%.*]] : @guaranteed $TreeInt): // CHECK: [[METATYPE:%.*]] = metatype $@thin TreeInt.Type // CHECK-NEXT: [[NIL:%.*]] = enum $TreeInt, #TreeInt.Nil!enumelt // CHECK-NOT: destroy_value [[NIL]] let _ = TreeInt.Nil // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeInt.Type // CHECK-NEXT: [[LEAF:%.*]] = enum $TreeInt, #TreeInt.Leaf!enumelt, [[ARG1]] // CHECK-NOT: destroy_value [[LEAF]] let _ = TreeInt.Leaf(t) // CHECK-NEXT: [[METATYPE:%.*]] = metatype $@thin TreeInt.Type // CHECK-NEXT: [[ARG2_COPY:%.*]] = copy_value [[ARG2]] : $TreeInt // CHECK-NEXT: [[ARG3_COPY:%.*]] = copy_value [[ARG3]] : $TreeInt // CHECK-NEXT: [[BOX:%.*]] = alloc_box ${ var (left: TreeInt, right: TreeInt) } // CHECK-NEXT: [[PB:%.*]] = project_box [[BOX]] // CHECK-NEXT: [[LEFT:%.*]] = tuple_element_addr [[PB]] // CHECK-NEXT: [[RIGHT:%.*]] = tuple_element_addr [[PB]] // CHECK-NEXT: store [[ARG2_COPY]] to [init] [[LEFT]] // CHECK-NEXT: store [[ARG3_COPY]] to [init] [[RIGHT]] // CHECK-NEXT: [[BRANCH:%.*]] = enum $TreeInt, #TreeInt.Branch!enumelt, [[BOX]] // CHECK-NEXT: destroy_value [[BRANCH]] let _ = TreeInt.Branch(left: l, right: r) } // CHECK: } // end sil function '$s13indirect_enum13TreeInt_cases_1l1rySi_AA0cD0OAFtF' enum TreeInt { case Nil case Leaf(Int) indirect case Branch(left: TreeInt, right: TreeInt) } enum TrivialButIndirect { case Direct(Int) indirect case Indirect(Int) } func a() {} func b(_ x: T) {} func c(_ x: T, _ y: T) {} func d() {} // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum11switchTreeAyyAA0D1AOyxGlF : $@convention(thin) (@guaranteed TreeA) -> () { func switchTreeA(_ x: TreeA) { // CHECK: bb0([[ARG:%.*]] : @guaranteed $TreeA): // -- x +0 // CHECK: switch_enum [[ARG]] : $TreeA, // CHECK: case #TreeA.Nil!enumelt: [[NIL_CASE:bb1]], // CHECK: case #TreeA.Leaf!enumelt: [[LEAF_CASE:bb2]], // CHECK: case #TreeA.Branch!enumelt: [[BRANCH_CASE:bb3]], switch x { // CHECK: [[NIL_CASE]]: // CHECK: function_ref @$s13indirect_enum1ayyF // CHECK: br [[OUTER_CONT:bb[0-9]+]] case .Nil: a() // CHECK: [[LEAF_CASE]]([[LEAF_BOX:%.*]] : @guaranteed $<τ_0_0> { var τ_0_0 } ): // CHECK: [[VALUE:%.*]] = project_box [[LEAF_BOX]] // CHECK: copy_addr [[VALUE]] to [initialization] [[X:%.*]] : $*T // CHECK: function_ref @$s13indirect_enum1b{{[_0-9a-zA-Z]*}}F // CHECK: destroy_addr [[X]] // CHECK: dealloc_stack [[X]] // -- x +0 // CHECK: br [[OUTER_CONT]] case .Leaf(let x): b(x) // CHECK: [[BRANCH_CASE]]([[NODE_BOX:%.*]] : @guaranteed $<τ_0_0> { var (left: TreeA<τ_0_0>, right: TreeA<τ_0_0>) } ): // CHECK: [[TUPLE_ADDR:%.*]] = project_box [[NODE_BOX]] // CHECK: [[TUPLE:%.*]] = load_borrow [[TUPLE_ADDR]] // CHECK: ([[LEFT:%.*]], [[RIGHT:%.*]]) = destructure_tuple [[TUPLE]] // CHECK: switch_enum [[LEFT]] : $TreeA, // CHECK: case #TreeA.Leaf!enumelt: [[LEAF_CASE_LEFT:bb[0-9]+]], // CHECK: default [[FAIL_LEFT:bb[0-9]+]] // CHECK: [[LEAF_CASE_LEFT]]([[LEFT_LEAF_BOX:%.*]] : @guaranteed $<τ_0_0> { var τ_0_0 } ): // CHECK: [[LEFT_LEAF_VALUE:%.*]] = project_box [[LEFT_LEAF_BOX]] // CHECK: switch_enum [[RIGHT]] : $TreeA, // CHECK: case #TreeA.Leaf!enumelt: [[LEAF_CASE_RIGHT:bb[0-9]+]], // CHECK: default [[FAIL_RIGHT:bb[0-9]+]] // CHECK: [[LEAF_CASE_RIGHT]]([[RIGHT_LEAF_BOX:%.*]] : @guaranteed $<τ_0_0> { var τ_0_0 } ): // CHECK: [[RIGHT_LEAF_VALUE:%.*]] = project_box [[RIGHT_LEAF_BOX]] // CHECK: copy_addr [[LEFT_LEAF_VALUE]] // CHECK: copy_addr [[RIGHT_LEAF_VALUE]] // -- x +1 // CHECK: br [[OUTER_CONT]] // CHECK: [[FAIL_RIGHT]]([[DEFAULT_VAL:%.*]] : @guaranteed // CHECK: br [[DEFAULT:bb[0-9]+]] // CHECK: [[FAIL_LEFT]]([[DEFAULT_VAL:%.*]] : @guaranteed // CHECK: br [[DEFAULT]] case .Branch(.Leaf(let x), .Leaf(let y)): c(x, y) // CHECK: [[DEFAULT]]: // -- x +0 default: d() } // CHECK: [[OUTER_CONT:%.*]]: // -- x +0 } // CHECK: } // end sil function '$s13indirect_enum11switchTreeAyyAA0D1AOyxGlF' // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum11switchTreeB{{[_0-9a-zA-Z]*}}F func switchTreeB(_ x: TreeB) { // CHECK: copy_addr %0 to [initialization] [[SCRATCH:%.*]] : // CHECK: switch_enum_addr [[SCRATCH]] switch x { // CHECK: bb{{.*}}: // CHECK: function_ref @$s13indirect_enum1ayyF // CHECK: destroy_addr [[SCRATCH]] // CHECK: dealloc_stack [[SCRATCH]] // CHECK: br [[OUTER_CONT:bb[0-9]+]] case .Nil: a() // CHECK: bb{{.*}}: // CHECK: copy_addr [[SCRATCH]] to [initialization] [[LEAF_COPY:%.*]] : // CHECK: [[LEAF_ADDR:%.*]] = unchecked_take_enum_data_addr [[LEAF_COPY]] // CHECK: copy_addr [take] [[LEAF_ADDR]] to [initialization] [[LEAF:%.*]] : // CHECK: function_ref @$s13indirect_enum1b{{[_0-9a-zA-Z]*}}F // CHECK: destroy_addr [[LEAF]] // CHECK: dealloc_stack [[LEAF]] // CHECK-NOT: destroy_addr [[LEAF_COPY]] // CHECK: dealloc_stack [[LEAF_COPY]] // CHECK: destroy_addr [[SCRATCH]] // CHECK: dealloc_stack [[SCRATCH]] // CHECK: br [[OUTER_CONT]] case .Leaf(let x): b(x) // CHECK: bb{{.*}}: // CHECK: copy_addr [[SCRATCH]] to [initialization] [[TREE_COPY:%.*]] : // CHECK: [[TREE_ADDR:%.*]] = unchecked_take_enum_data_addr [[TREE_COPY]] // -- box +1 immutable // CHECK: [[BOX:%.*]] = load [take] [[TREE_ADDR]] // CHECK: [[TUPLE:%.*]] = project_box [[BOX]] // CHECK: [[LEFT:%.*]] = tuple_element_addr [[TUPLE]] // CHECK: [[RIGHT:%.*]] = tuple_element_addr [[TUPLE]] // CHECK: switch_enum_addr [[LEFT]] {{.*}}, default [[LEFT_FAIL:bb[0-9]+]] // CHECK: bb{{.*}}: // CHECK: copy_addr [[LEFT]] to [initialization] [[LEFT_COPY:%.*]] : // CHECK: [[LEFT_LEAF:%.*]] = unchecked_take_enum_data_addr [[LEFT_COPY]] : $*TreeB, #TreeB.Leaf // CHECK: switch_enum_addr [[RIGHT]] {{.*}}, default [[RIGHT_FAIL:bb[0-9]+]] // CHECK: bb{{.*}}: // CHECK: copy_addr [[RIGHT]] to [initialization] [[RIGHT_COPY:%.*]] : // CHECK: [[RIGHT_LEAF:%.*]] = unchecked_take_enum_data_addr [[RIGHT_COPY]] : $*TreeB, #TreeB.Leaf // CHECK: copy_addr [take] [[LEFT_LEAF]] to [initialization] [[X:%.*]] : // CHECK: copy_addr [take] [[RIGHT_LEAF]] to [initialization] [[Y:%.*]] : // CHECK: function_ref @$s13indirect_enum1c{{[_0-9a-zA-Z]*}}F // CHECK: destroy_addr [[Y]] // CHECK: dealloc_stack [[Y]] // CHECK: destroy_addr [[X]] // CHECK: dealloc_stack [[X]] // CHECK-NOT: destroy_addr [[RIGHT_COPY]] // CHECK: dealloc_stack [[RIGHT_COPY]] // CHECK-NOT: destroy_addr [[LEFT_COPY]] // CHECK: dealloc_stack [[LEFT_COPY]] // -- box +0 // CHECK: destroy_value [[BOX]] // CHECK-NOT: destroy_addr [[TREE_COPY]] // CHECK: dealloc_stack [[TREE_COPY]] // CHECK: destroy_addr [[SCRATCH]] // CHECK: dealloc_stack [[SCRATCH]] case .Branch(.Leaf(let x), .Leaf(let y)): c(x, y) // CHECK: [[RIGHT_FAIL]]: // CHECK: destroy_addr [[LEFT_LEAF]] // CHECK-NOT: destroy_addr [[LEFT_COPY]] // CHECK: dealloc_stack [[LEFT_COPY]] // CHECK: destroy_value [[BOX]] // CHECK-NOT: destroy_addr [[TREE_COPY]] // CHECK: dealloc_stack [[TREE_COPY]] // CHECK: br [[INNER_CONT:bb[0-9]+]] // CHECK: [[LEFT_FAIL]]: // CHECK: destroy_value [[BOX]] // CHECK-NOT: destroy_addr [[TREE_COPY]] // CHECK: dealloc_stack [[TREE_COPY]] // CHECK: br [[INNER_CONT:bb[0-9]+]] // CHECK: [[INNER_CONT]]: // CHECK: function_ref @$s13indirect_enum1dyyF // CHECK: destroy_addr [[SCRATCH]] // CHECK: dealloc_stack [[SCRATCH]] // CHECK: br [[OUTER_CONT]] default: d() } // CHECK: [[OUTER_CONT]]: // CHECK: return } // Make sure that switchTreeInt obeys ownership invariants. // // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum13switchTreeInt{{[_0-9a-zA-Z]*}}F func switchTreeInt(_ x: TreeInt) { switch x { case .Nil: a() case .Leaf(let x): b(x) case .Branch(.Leaf(let x), .Leaf(let y)): c(x, y) default: d() } } // CHECK: } // end sil function '$s13indirect_enum13switchTreeInt{{[_0-9a-zA-Z]*}}F' // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum10guardTreeA{{[_0-9a-zA-Z]*}}F func guardTreeA(_ tree: TreeA) { // CHECK: bb0([[ARG:%.*]] : @guaranteed $TreeA): do { // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TreeA, case #TreeA.Nil!enumelt: [[YES:bb[0-9]+]], default [[NO1:bb[0-9]+]] // CHECK: [[YES]]: guard case .Nil = tree else { return } // CHECK: [[X:%.*]] = alloc_stack $T // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TreeA, case #TreeA.Leaf!enumelt: [[YES:bb[0-9]+]], default [[NO2:bb[0-9]+]] // CHECK: [[YES]]([[BOX:%.*]] : @owned $<τ_0_0> { var τ_0_0 } ): // CHECK: [[VALUE_ADDR:%.*]] = project_box [[BOX]] // CHECK: [[TMP:%.*]] = alloc_stack // CHECK: copy_addr [[VALUE_ADDR]] to [initialization] [[TMP]] // CHECK: copy_addr [take] [[TMP]] to [initialization] [[X]] // CHECK: destroy_value [[BOX]] guard case .Leaf(let x) = tree else { return } // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TreeA, case #TreeA.Branch!enumelt: [[YES:bb[0-9]+]], default [[NO3:bb[0-9]+]] // CHECK: [[YES]]([[BOX:%.*]] : @owned $<τ_0_0> { var (left: TreeA<τ_0_0>, right: TreeA<τ_0_0>) } ): // CHECK: [[VALUE_ADDR:%.*]] = project_box [[BOX]] // CHECK: [[TUPLE:%.*]] = load_borrow [[VALUE_ADDR]] // CHECK: [[TUPLE_COPY:%.*]] = copy_value [[TUPLE]] // CHECK: end_borrow [[TUPLE]] // CHECK: ([[L:%.*]], [[R:%.*]]) = destructure_tuple [[TUPLE_COPY]] // CHECK: destroy_value [[BOX]] guard case .Branch(left: let l, right: let r) = tree else { return } // CHECK: destroy_value [[R]] // CHECK: destroy_value [[L]] // CHECK: destroy_addr [[X]] } do { // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TreeA, case #TreeA.Nil!enumelt: [[YES:bb[0-9]+]], default [[NO4:bb[0-9]+]] // CHECK: [[NO4]]([[ORIGINAL_VALUE:%.*]] : @owned $TreeA): // CHECK: destroy_value [[ORIGINAL_VALUE]] // CHECK: [[YES]]: // CHECK: br if case .Nil = tree { } // CHECK: [[X:%.*]] = alloc_stack $T // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TreeA, case #TreeA.Leaf!enumelt: [[YES:bb[0-9]+]], default [[NO5:bb[0-9]+]] // CHECK: [[NO5]]([[ORIGINAL_VALUE:%.*]] : @owned $TreeA): // CHECK: destroy_value [[ORIGINAL_VALUE]] // CHECK: [[YES]]([[BOX:%.*]] : @owned $<τ_0_0> { var τ_0_0 } ): // CHECK: [[VALUE_ADDR:%.*]] = project_box [[BOX]] // CHECK: [[TMP:%.*]] = alloc_stack // CHECK: copy_addr [[VALUE_ADDR]] to [initialization] [[TMP]] // CHECK: copy_addr [take] [[TMP]] to [initialization] [[X]] // CHECK: destroy_value [[BOX]] // CHECK: destroy_addr [[X]] if case .Leaf(let x) = tree { } // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TreeA, case #TreeA.Branch!enumelt: [[YES:bb[0-9]+]], default [[NO:bb[0-9]+]] // CHECK: [[NO]]([[ORIGINAL_VALUE:%.*]] : @owned $TreeA): // CHECK: destroy_value [[ORIGINAL_VALUE]] // CHECK: [[YES]]([[BOX:%.*]] : @owned $<τ_0_0> { var (left: TreeA<τ_0_0>, right: TreeA<τ_0_0>) } ): // CHECK: [[VALUE_ADDR:%.*]] = project_box [[BOX]] // CHECK: [[TUPLE:%.*]] = load_borrow [[VALUE_ADDR]] // CHECK: [[TUPLE_COPY:%.*]] = copy_value [[TUPLE]] // CHECK: end_borrow [[TUPLE]] // CHECK: ([[L:%.*]], [[R:%.*]]) = destructure_tuple [[TUPLE_COPY]] // CHECK: destroy_value [[BOX]] // CHECK: destroy_value [[R]] // CHECK: destroy_value [[L]] if case .Branch(left: let l, right: let r) = tree { } } // CHECK: [[NO3]]([[ORIGINAL_VALUE:%.*]] : @owned $TreeA): // CHECK: destroy_value [[ORIGINAL_VALUE]] // CHECK: [[NO2]]([[ORIGINAL_VALUE:%.*]] : @owned $TreeA): // CHECK: destroy_value [[ORIGINAL_VALUE]] // CHECK: [[NO1]]([[ORIGINAL_VALUE:%.*]] : @owned $TreeA): // CHECK: destroy_value [[ORIGINAL_VALUE]] } // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum10guardTreeB{{[_0-9a-zA-Z]*}}F func guardTreeB(_ tree: TreeB) { do { // CHECK: copy_addr %0 to [initialization] [[TMP1:%.*]] : // CHECK: switch_enum_addr [[TMP1]] : $*TreeB, case #TreeB.Nil!enumelt: [[YES:bb[0-9]+]], default [[NO2:bb[0-9]+]] // CHECK: [[YES]]: // CHECK: destroy_addr [[TMP1]] guard case .Nil = tree else { return } // CHECK: [[X:%.*]] = alloc_stack $T // CHECK: copy_addr %0 to [initialization] [[TMP2:%.*]] : // CHECK: switch_enum_addr [[TMP2]] : $*TreeB, case #TreeB.Leaf!enumelt: [[YES:bb[0-9]+]], default [[NO2:bb[0-9]+]] // CHECK: [[YES]]: // CHECK: [[VALUE:%.*]] = unchecked_take_enum_data_addr [[TMP2]] // CHECK: copy_addr [take] [[VALUE]] to [initialization] [[X]] // CHECK: dealloc_stack [[TMP2]] guard case .Leaf(let x) = tree else { return } // CHECK: [[L:%.*]] = alloc_stack $TreeB // CHECK: [[R:%.*]] = alloc_stack $TreeB // CHECK: copy_addr %0 to [initialization] [[TMP3:%.*]] : // CHECK: switch_enum_addr [[TMP3]] : $*TreeB, case #TreeB.Branch!enumelt: [[YES:bb[0-9]+]], default [[NO3:bb[0-9]+]] // CHECK: [[YES]]: // CHECK: [[BOX_ADDR:%.*]] = unchecked_take_enum_data_addr [[TMP3]] // CHECK: [[BOX:%.*]] = load [take] [[BOX_ADDR]] // CHECK: [[TUPLE_ADDR:%.*]] = project_box [[BOX]] // CHECK: copy_addr [[TUPLE_ADDR]] to [initialization] [[TUPLE_COPY:%.*]] : // CHECK: [[L_COPY:%.*]] = tuple_element_addr [[TUPLE_COPY]] // CHECK: copy_addr [take] [[L_COPY]] to [initialization] [[L]] // CHECK: [[R_COPY:%.*]] = tuple_element_addr [[TUPLE_COPY]] // CHECK: copy_addr [take] [[R_COPY]] to [initialization] [[R]] // CHECK: destroy_value [[BOX]] guard case .Branch(left: let l, right: let r) = tree else { return } // CHECK: destroy_addr [[R]] // CHECK: destroy_addr [[L]] // CHECK: destroy_addr [[X]] } do { // CHECK: copy_addr %0 to [initialization] [[TMP:%.*]] : // CHECK: switch_enum_addr [[TMP]] : $*TreeB, case #TreeB.Nil!enumelt: [[YES:bb[0-9]+]], default [[NO:bb[0-9]+]] // CHECK: [[NO]]: // CHECK: destroy_addr [[TMP]] // CHECK: [[YES]]: // CHECK: destroy_addr [[TMP]] if case .Nil = tree { } // CHECK: [[X:%.*]] = alloc_stack $T // CHECK: copy_addr %0 to [initialization] [[TMP:%.*]] : // CHECK: switch_enum_addr [[TMP]] : $*TreeB, case #TreeB.Leaf!enumelt: [[YES:bb[0-9]+]], default [[NO:bb[0-9]+]] // CHECK: [[NO]]: // CHECK: destroy_addr [[TMP]] // CHECK: [[YES]]: // CHECK: [[VALUE:%.*]] = unchecked_take_enum_data_addr [[TMP]] // CHECK: copy_addr [take] [[VALUE]] to [initialization] [[X]] // CHECK: dealloc_stack [[TMP]] // CHECK: destroy_addr [[X]] if case .Leaf(let x) = tree { } // CHECK: [[L:%.*]] = alloc_stack $TreeB // CHECK: [[R:%.*]] = alloc_stack $TreeB // CHECK: copy_addr %0 to [initialization] [[TMP:%.*]] : // CHECK: switch_enum_addr [[TMP]] : $*TreeB, case #TreeB.Branch!enumelt: [[YES:bb[0-9]+]], default [[NO:bb[0-9]+]] // CHECK: [[NO]]: // CHECK: destroy_addr [[TMP]] // CHECK: [[YES]]: // CHECK: [[BOX_ADDR:%.*]] = unchecked_take_enum_data_addr [[TMP]] // CHECK: [[BOX:%.*]] = load [take] [[BOX_ADDR]] // CHECK: [[TUPLE_ADDR:%.*]] = project_box [[BOX]] // CHECK: copy_addr [[TUPLE_ADDR]] to [initialization] [[TUPLE_COPY:%.*]] : // CHECK: [[L_COPY:%.*]] = tuple_element_addr [[TUPLE_COPY]] // CHECK: copy_addr [take] [[L_COPY]] to [initialization] [[L]] // CHECK: [[R_COPY:%.*]] = tuple_element_addr [[TUPLE_COPY]] // CHECK: copy_addr [take] [[R_COPY]] to [initialization] [[R]] // CHECK: destroy_value [[BOX]] // CHECK: destroy_addr [[R]] // CHECK: destroy_addr [[L]] if case .Branch(left: let l, right: let r) = tree { } } // CHECK: [[NO3]]: // CHECK: destroy_addr [[TMP3]] // CHECK: [[NO2]]: // CHECK: destroy_addr [[TMP2]] // CHECK: [[NO1]]: // CHECK: destroy_addr [[TMP1]] } // Just run guardTreeInt through the ownership verifier // // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum12guardTreeInt{{[_0-9a-zA-Z]*}}F func guardTreeInt(_ tree: TreeInt) { do { guard case .Nil = tree else { return } guard case .Leaf(let x) = tree else { return } guard case .Branch(left: let l, right: let r) = tree else { return } } do { if case .Nil = tree { } if case .Leaf(let x) = tree { } if case .Branch(left: let l, right: let r) = tree { } } } // SEMANTIC ARC TODO: This test needs to be made far more comprehensive. // CHECK-LABEL: sil hidden [ossa] @$s13indirect_enum35dontDisableCleanupOfIndirectPayloadyyAA010TrivialButG0OF : $@convention(thin) (@guaranteed TrivialButIndirect) -> () { func dontDisableCleanupOfIndirectPayload(_ x: TrivialButIndirect) { // CHECK: bb0([[ARG:%.*]] : @guaranteed $TrivialButIndirect): // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TrivialButIndirect, case #TrivialButIndirect.Direct!enumelt: [[YES:bb[0-9]+]], case #TrivialButIndirect.Indirect!enumelt: [[NO:bb[0-9]+]] // guard case .Direct(let foo) = x else { return } // CHECK: [[YES]]({{%.*}} : $Int): // CHECK: [[ARG_COPY:%.*]] = copy_value [[ARG]] // CHECK: switch_enum [[ARG_COPY]] : $TrivialButIndirect, case #TrivialButIndirect.Indirect!enumelt: [[YES:bb[0-9]+]], case #TrivialButIndirect.Direct!enumelt: [[NO2:bb[0-9]+]] // CHECK: [[YES]]([[BOX:%.*]] : @owned ${ var Int }): // CHECK: destroy_value [[BOX]] // CHECK: [[NO2]]({{%.*}} : $Int): // CHECK-NOT: destroy_value // CHECK: [[NO]]([[PAYLOAD:%.*]] : @owned ${ var Int }): // CHECK: destroy_value [[PAYLOAD]] guard case .Indirect(let bar) = x else { return } } // CHECK: } // end sil function '$s13indirect_enum35dontDisableCleanupOfIndirectPayloadyyAA010TrivialButG0OF' // Make sure that in these cases we do not break any ownership invariants. class Box { var value: T init(_ inputValue: T) { value = inputValue } } enum ValueWithInlineStorage { case inline(T) indirect case box(Box) } func switchValueWithInlineStorage(v: ValueWithInlineStorage) { switch v { case .inline: return case .box(let box): return } } func guardValueWithInlineStorage(v: ValueWithInlineStorage) { do { guard case .inline = v else { return } guard case .box(let box) = v else { return } } do { if case .inline = v { return } if case .box(let box) = v { return } } }