blob: 31cf09e54a651bc997a6c17c6acfdc1d8c61d9f8 [file] [log] [blame]
// RUN: %target-sil-opt -enable-sil-ownership -enable-sil-verify-all %s -definite-init -raw-sil-inst-lowering -verify | %FileCheck %s
// This file contains tests that test diagnostics emitted for var initialization
// by DI.
sil_stage raw
import Builtin
import Swift
sil @takes_Int_inout : $@convention(thin) (@inout Int) -> ()
sil @makesInt : $@convention(thin) () -> Int
// CHECK-LABEL: sil @use_before_init
sil @use_before_init : $@convention(thin) () -> Int {
bb0:
%0 = alloc_box $_0_0> { var τ_0_0 } <Int>
%0a = project_box %0 : $_0_0> { var τ_0_0 } <Int>, 0
%1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}}
%4 = load [trivial] %1 : $*Int // expected-error {{variable '<unknown>' used before being initialized}}
destroy_value %0 : $_0_0> { var τ_0_0 } <Int>
return %4 : $Int
}
// CHECK-LABEL: @inout_uninit
sil @inout_uninit : $@convention(thin) () -> () {
bb0:
%0 = alloc_box $_0_0> { var τ_0_0 } <Int>
%0a = project_box %0 : $_0_0> { var τ_0_0 } <Int>, 0
%1 = mark_uninitialized [var] %0a : $*Int // expected-note {{variable defined here}}
%5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> ()
%6 = apply %5(%1) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '<unknown>' passed by reference before being initialized}}
destroy_value %0 : $_0_0> { var τ_0_0 } <Int>
%t = tuple ()
return %t : $()
}
// This function shouldn't produce any diagnostics.
//
// func used_by_inout(a : Int) -> (Int, Int) {
// var t = a
// takes_Int_inout(&a)
// return (t, a)
//}
// CHECK-LABEL: sil @used_by_inout
sil @used_by_inout : $@convention(thin) (Int) -> (Int, Int) {
bb0(%0 : @trivial $Int):
%91 = alloc_box $_0_0> { var τ_0_0 } <Int>
%91a = project_box %91 : $_0_0> { var τ_0_0 } <Int>, 0
%1 = mark_uninitialized [var] %91a : $*Int
store %0 to [trivial] %1 : $*Int
%3 = load [trivial] %1 : $*Int
%5 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> ()
%6 = apply %5(%1) : $@convention(thin) (@inout Int) -> ()
%7 = load [trivial] %1 : $*Int
%8 = tuple (%3 : $Int, %7 : $Int)
destroy_value %91 : $_0_0> { var τ_0_0 } <Int>
return %8 : $(Int, Int)
}
struct AddressOnlyStruct {
var a : Any
var b : Int
}
/// returns_generic_struct - This returns a struct by reference.
sil @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct
// There should be no error in this function.
// CHECK-LABEL: sil @call_struct_return_function
sil @call_struct_return_function : $@convention(thin) () -> Int {
bb0:
%0 = alloc_box $_0_0> { var τ_0_0 } <AddressOnlyStruct>
%0a = project_box %0 : $_0_0> { var τ_0_0 } <AddressOnlyStruct>, 0
%1 = mark_uninitialized [var] %0a : $*AddressOnlyStruct
%2 = function_ref @returns_generic_struct : $@convention(thin) () -> @out AddressOnlyStruct
%3 = apply %2(%1) : $@convention(thin) () -> @out AddressOnlyStruct
%4 = struct_element_addr %1 : $*AddressOnlyStruct, #AddressOnlyStruct.b
%5 = load [trivial] %4 : $*Int
destroy_value %0 : $_0_0> { var τ_0_0 } <AddressOnlyStruct>
return %5 : $Int
}
// CHECK-LABEL: sil @tuple_elements1
sil @tuple_elements1 : $@convention(thin) (Int) -> () {
bb0(%0 : @trivial $Int):
%2 = alloc_box $_0_0> { var τ_0_0 } <(Int, Int)>
%2a = project_box %2 : $_0_0> { var τ_0_0 } <(Int, Int)>, 0
%3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}}
%4 = tuple_element_addr %3 : $*(Int, Int), 0
%5 = tuple_element_addr %3 : $*(Int, Int), 1
%14 = function_ref @takes_Int_inout : $@convention(thin) (@inout Int) -> ()
%15 = tuple_element_addr %3 : $*(Int, Int), 1
%16 = apply %14(%15) : $@convention(thin) (@inout Int) -> () // expected-error {{variable '<unknown>.1' passed by reference before being initialized}}
destroy_value %2 : $_0_0> { var τ_0_0 } <(Int, Int)>
%99 = tuple ()
return %99 : $()
}
// CHECK-LABEL: sil @tuple_elements2
sil @tuple_elements2 : $@convention(thin) (Int) -> (Int, Int) {
bb0(%0 : @trivial $Int):
%2 = alloc_box $_0_0> { var τ_0_0 } <(Int, Int)>
%2a = project_box %2 : $_0_0> { var τ_0_0 } <(Int, Int)>, 0
%3 = mark_uninitialized [var] %2a : $*(Int, Int) // expected-note {{variable defined here}}
%18 = tuple_element_addr %3 : $*(Int, Int), 0
store %0 to [trivial] %18 : $*Int
%20 = load [trivial] %3 : $*(Int, Int) // expected-error {{variable '<unknown>.1' used before being initialized}}
%21 = tuple_extract %20 : $(Int, Int), 0
%22 = tuple_extract %20 : $(Int, Int), 1
%23 = tuple (%21 : $Int, %22 : $Int)
destroy_value %2 : $_0_0> { var τ_0_0 } <(Int, Int)>
return %23 : $(Int, Int)
}
// CHECK-LABEL: sil @copy_addr1
sil @copy_addr1 : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : @trivial $*T, %1 : @trivial $*T):
%3 = alloc_box $_0_0> { var τ_0_0 } <T>
%3a = project_box %3 : $_0_0> { var τ_0_0 } <T>, 0
%4 = mark_uninitialized [var] %3a : $*T
copy_addr [take] %1 to [initialization] %4 : $*T
copy_addr %4 to [initialization] %0 : $*T
destroy_value %3 : $_0_0> { var τ_0_0 } <T>
%9 = tuple ()
return %9 : $()
}
// CHECK-LABEL: sil @copy_addr2
sil @copy_addr2 : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : @trivial $*T, %1 : @trivial $*T):
%3 = alloc_box $_0_0> { var τ_0_0 } <T>
%3a = project_box %3 : $_0_0> { var τ_0_0 } <T>, 0
%4 = mark_uninitialized [var] %3a : $*T // expected-note {{variable defined here}}
copy_addr %4 to [initialization] %0 : $*T // expected-error {{variable '<unknown>' used before being initialized}}
destroy_value %3 : $_0_0> { var τ_0_0 } <T>
%9 = tuple ()
return %9 : $()
}
sil @takes_closure : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
sil @closure0 : $@convention(thin) (@owned _0_0> { var τ_0_0 } <Int>) -> ()
// CHECK-LABEL: sil @closure_test
sil @closure_test : $@convention(thin) () -> () {
bb0:
%1 = alloc_box $_0_0> { var τ_0_0 } <Int>
%1a = project_box %1 : $_0_0> { var τ_0_0 } <Int>, 0
%0 = mark_uninitialized [var] %1a : $*Int // expected-note {{variable defined here}}
%5 = function_ref @takes_closure : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
%6 = function_ref @closure0 : $@convention(thin) (@owned _0_0> { var τ_0_0 } <Int>) -> ()
%1copy = copy_value %1 : $_0_0> { var τ_0_0 } <Int>
mark_function_escape %0 : $*Int // expected-error {{variable '<unknown>' used by function definition before being initialized}}
%8 = partial_apply %6(%1copy) : $@convention(thin) (@owned _0_0> { var τ_0_0 } <Int>) -> ()
%9 = apply %5(%8) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
destroy_value %1 : $_0_0> { var τ_0_0 } <Int>
%11 = tuple ()
return %11 : $()
}
class SomeClass {}
sil @getSomeClass : $@convention(thin) () -> @owned SomeClass
sil @getSomeOptionalClass : $@convention(thin) () -> @owned Optional<SomeClass>
// CHECK-LABEL: sil @assign_test_trivial
sil @assign_test_trivial : $@convention(thin) (Int) -> Int {
bb0(%0 : @trivial $Int):
%7 = alloc_box $_0_0> { var τ_0_0 } <Int>
%7a = project_box %7 : $_0_0> { var τ_0_0 } <Int>, 0
%1 = mark_uninitialized [var] %7a : $*Int
// These assigns are a mix of init + store forms, but because Int is trivial,
// they all turn into stores.
assign %0 to %1 : $*Int
assign %0 to %1 : $*Int
assign %0 to %1 : $*Int
%2 = load [trivial] %1 : $*Int
destroy_value %7 : $_0_0> { var τ_0_0 } <Int>
return %2 : $Int
}
// CHECK-LABEL: sil @assign_test_nontrivial
sil @assign_test_nontrivial : $@convention(thin) () -> () {
bb0:
// Assignments of nontrivial types. The first becomes an initialize (i.e.,
// lone store), the second becomes an assignment (retain/release dance).
%b = alloc_box $_0_0> { var τ_0_0 } <SomeClass>
%ba = project_box %b : $_0_0> { var τ_0_0 } <SomeClass>, 0
%c = mark_uninitialized [var] %ba : $*SomeClass
%f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
%4 = apply %f() : $@convention(thin) () -> @owned SomeClass
// CHECK: [[C1:%[0-9]+]] = apply
assign %4 to %c : $*SomeClass
// CHECK-NEXT: store
%8 = apply %f() : $@convention(thin) () -> @owned SomeClass
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
assign %8 to %c : $*SomeClass
// CHECK-NEXT: load
// CHECK-NEXT: store [[C2]]
// CHECK-NEXT: destroy_value
destroy_addr %c : $*SomeClass
// CHECK-NEXT: destroy_addr
dealloc_box %b : $_0_0> { var τ_0_0 } <SomeClass>
%11 = tuple ()
return %11 : $()
}
// CHECK-LABEL: sil @assign_test_addressonly
sil @assign_test_addressonly : $@convention(thin) <T> (@in T) -> @out T {
bb0(%0 : @trivial $*T, %1 :@trivial $*T):
%b = alloc_box $_0_0> { var τ_0_0 } <T>
%ba = project_box %b : $_0_0> { var τ_0_0 } <T>, 0
%2 = mark_uninitialized [var] %ba : $*T
// CHECK: [[PB:%[0-9]+]] = project_box
// This should become an initialization of %4
copy_addr %1 to %2 : $*T
// CHECK-NEXT: copy_addr %1 to [initialization] [[PB]] : $*T
// This should stay an assignment of %4
copy_addr [take] %1 to %2 : $*T
// CHECK-NEXT: copy_addr [take] %1 to [[PB]] : $*T
// This is a load, and shouldn't be changed.
copy_addr %2 to [initialization] %0 : $*T
// CHECK-NEXT: copy_addr [[PB]] to [initialization] %0 : $*T
destroy_value %b : $_0_0> { var τ_0_0 } <T>
// CHECK-NEXT: destroy_value %2
%9 = tuple ()
return %9 : $()
}
// CHECK-LABEL: sil @assign_test_weak
sil @assign_test_weak : $@convention(thin) () -> () {
bb0:
// Assignments of weak pointer. The first becomes an initialize, and the
// second becomes an assignment.
%b = alloc_box $_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>
%ba = project_box %b : $_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>, 0
%c = mark_uninitialized [var] %ba : $*@sil_weak Optional<SomeClass> // expected-note {{variable defined here}}
// Invalid load to keep the alloc_box around so we can check init semantics.
%c_loaded = load_weak %c : $*@sil_weak Optional<SomeClass> // expected-error {{used before being initialized}}
destroy_value %c_loaded : $Optional<SomeClass>
%f = function_ref @getSomeOptionalClass : $@convention(thin) () -> @owned Optional<SomeClass>
%4 = apply %f() : $@convention(thin) () -> @owned Optional<SomeClass>
// CHECK: [[C1:%[0-9]+]] = apply
// This should become an initialization.
store_weak %4 to %c : $*@sil_weak Optional<SomeClass>
// CHECK-NEXT: store_weak [[C1]] to [initialization] %1
destroy_value %4 : $Optional<SomeClass>
// CHECK-NEXT: destroy_value [[C1]]
%8 = apply %f() : $@convention(thin) () -> @owned Optional<SomeClass>
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
store_weak %8 to %c : $*@sil_weak Optional<SomeClass>
// CHECK-NEXT: store_weak [[C2]] to %1
destroy_value %8 : $Optional<SomeClass>
// CHECK-NEXT: destroy_value [[C2]]
destroy_value %b : $_0_0> { var τ_0_0 } <@sil_weak Optional<SomeClass>>
%11 = tuple ()
return %11 : $()
}
// CHECK-LABEL: sil @assign_test_unowned
sil @assign_test_unowned : $@convention(thin) () -> () {
bb0:
// Assignments of unowned pointer. The first becomes an initialize, and the
// second becomes an assignment.
%b = alloc_box $_0_0> { var τ_0_0 } <@sil_unowned SomeClass>
%ba = project_box %b : $_0_0> { var τ_0_0 } <@sil_unowned SomeClass>, 0
%c = mark_uninitialized [var] %ba : $*@sil_unowned SomeClass
%f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
%4 = apply %f() : $@convention(thin) () -> @owned SomeClass
// CHECK: [[C1:%[0-9]+]] = apply
// This should become an initialization.
%5 = ref_to_unowned %4 : $SomeClass to $@sil_unowned SomeClass
// CHECK-NEXT: [[C1u:%[0-9]+]] = ref_to_unowned [[C1]]
%5a = copy_value %5 : $@sil_unowned SomeClass
// CHECK-NEXT: [[C1u_copy:%.*]] = copy_value [[C1u]]
assign %5a to %c : $*@sil_unowned SomeClass
// CHECK-NEXT: store [[C1u_copy]] to [init]
destroy_value %4 : $SomeClass
// CHECK-NEXT: destroy_value [[C1]]
%8 = apply %f() : $@convention(thin) () -> @owned SomeClass
// CHECK-NEXT: [[C2:%[0-9]+]] = apply
%9 = ref_to_unowned %8 : $SomeClass to $@sil_unowned SomeClass
// CHECK: [[C2u:%[0-9]+]] = ref_to_unowned [[C2]]
%9a = copy_value %9 : $@sil_unowned SomeClass
// CHECK-NEXT: copy_value [[C2u]]
assign %9a to %c : $*@sil_unowned SomeClass
// CHECK-NEXT: load
// CHECK-NEXT: store
// CHECK-NEXT: destroy_value
destroy_value %8 : $SomeClass
// CHECK-NEXT: destroy_value [[C2]]
destroy_addr %c : $*@sil_unowned SomeClass
dealloc_box %b : $_0_0> { var τ_0_0 } <@sil_unowned SomeClass>
%11 = tuple ()
return %11 : $()
}
struct ContainsNativeObject {
var x : Int
var y : Builtin.NativeObject
}
sil @test_struct : $@convention(thin) (@inout ContainsNativeObject) -> () {
bb0(%0 : @trivial $*ContainsNativeObject):
%b = alloc_box $_0_0> { var τ_0_0 } <ContainsNativeObject>
%ba = project_box %b : $_0_0> { var τ_0_0 } <ContainsNativeObject>, 0
%c = mark_uninitialized [var] %ba : $*ContainsNativeObject
%1 = load [copy] %0 : $*ContainsNativeObject
assign %1 to %c : $*ContainsNativeObject
destroy_value %b : $_0_0> { var τ_0_0 } <ContainsNativeObject>
%x = tuple ()
return %x : $()
}
// CHECK-LABEL: sil @non_box_assign_trivial
// CHECK-NOT: load
// CHECK: store
// CHECK: return
sil @non_box_assign_trivial : $@convention(thin) (@inout Bool, Bool) -> () {
bb0(%0 : @trivial $*Bool, %1 : @trivial $Bool):
assign %1 to %0 : $*Bool
%9 = tuple ()
return %9 : $()
}
// CHECK-LABEL: sil @non_box_assign
// CHECK: load
// CHECK: store
// CHECK: return
sil @non_box_assign : $@convention(thin) (@inout SomeClass, @owned SomeClass) -> () {
bb0(%0 : @trivial $*SomeClass, %1 : @owned $SomeClass):
assign %1 to %0 : $*SomeClass
%9 = tuple ()
return %9 : $()
}
sil_global @int_global : $Int
// CHECK-LABEL: sil @test_tlc
// CHECK-NOT: mark_uninitialized
// CHECK: return
sil @test_tlc : $() -> () {
%0 = global_addr @int_global : $*Int
%1 = mark_uninitialized [var] %0 : $*Int
%9 = tuple ()
return %9 : $()
}
struct XYStruct { var x, y : Int }
sil @init_xy_struct : $@convention(thin) () -> XYStruct
protocol P {}
class C : P {}
sil @use : $@convention(thin) (@in P) -> ()
// CHECK-LABEL: sil @release_not_constructed
sil @release_not_constructed : $@convention(thin) () -> () {
bb0: // CHECK: bb0:
%3 = alloc_stack $SomeClass
// CHECK-NEXT: alloc_stack
%c = mark_uninitialized [var] %3 : $*SomeClass
// This should get removed.
destroy_addr %c : $*SomeClass
dealloc_stack %3 : $*SomeClass
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: tuple ()
%15 = tuple ()
return %15 : $()
}
// CHECK-LABEL: sil @release_some_constructed
sil @release_some_constructed : $@convention(thin) () -> () {
bb0:
%0 = tuple ()
%b = alloc_stack $(SomeClass, SomeClass)
%1 = mark_uninitialized [var] %b : $*(SomeClass, SomeClass)
%2 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
%3 = apply %2() : $@convention(thin) () -> @owned SomeClass
%4 = tuple_element_addr %1 : $*(SomeClass, SomeClass), 0
store %3 to [init] %4 : $*SomeClass
// CHECK: store
// Only Element #0 should get released.
// CHECK-NEXT: [[ELT0:%[0-9]+]] = tuple_element_addr {{.*}}, 0
// CHECK-NEXT: destroy_addr [[ELT0]]
destroy_addr %1 : $*(SomeClass, SomeClass)
// CHECK-NEXT: dealloc_stack
dealloc_stack %b : $*(SomeClass, SomeClass)
%8 = tuple ()
return %8 : $()
}
// rdar://15379013
// CHECK-LABEL: sil @init_existential_with_class
sil @init_existential_with_class : $@convention(thin) (@inout C) -> () {
entry(%a : @trivial $*C):
%p = alloc_stack $P
%b = mark_uninitialized [var] %p : $*P
%q = init_existential_addr %b : $*P, $C
// CHECK: copy_addr {{.*}} to [initialization] %2 : $*C
copy_addr %a to [initialization] %q : $*C
%u = function_ref @use : $@convention(thin) (@in P) -> ()
%v = apply %u(%b) : $@convention(thin) (@in P) -> ()
dealloc_stack %p : $*P
%z = tuple ()
return %z : $()
}
// CHECK-LABEL: sil @conditional_init
// This test checks conditional destruction logic. Because the value is only
// initialized on some paths, we need to either hoist up the destroy_addr or
// emit a boolean control value to make sure the value is only destroyed if
// actually initialized.
sil @conditional_init : $@convention(thin) (Bool) -> () {
bb0(%0 : @trivial $Bool):
%2 = alloc_stack $SomeClass
%3 = mark_uninitialized [var] %2 : $*SomeClass
// CHECK: [[CONTROL:%[0-9]+]] = alloc_stack $Builtin.Int1
// CHECK: [[ZERO:%[0-9]+]] = integer_literal $Builtin.Int1, 0
// CHECK: store [[ZERO]] to [trivial] [[CONTROL]] : $*Builtin.Int1
%5 = integer_literal $Builtin.Int1, 1
cond_br %5, bb1, bb2
bb1:
// CHECK: bb1:
// CHECK: function_ref @getSomeClass
// CHECK: [[ONE:%[0-9]+]] = integer_literal $Builtin.Int1, -1
// CHECK: store [[ONE]] to [trivial] [[CONTROL]] : $*Builtin.Int1
%f = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
%6 = apply %f() : $@convention(thin) () -> @owned SomeClass
assign %6 to %3 : $*SomeClass
br bb2
bb2:
destroy_addr %3 : $*SomeClass
dealloc_stack %2 : $*SomeClass
%14 = tuple ()
return %14 : $()
}
// CHECK-LABEL: sil @conditionalInitOrAssign
sil @conditionalInitOrAssign : $@convention(thin) (Builtin.Int1) -> () {
bb0(%0 : @trivial $Builtin.Int1):
// CHECK: [[CONTROL:%[0-9]+]] = alloc_stack $Builtin.Int1
// CHECK: [[CLASSVAL:%[0-9]+]] = alloc_stack $SomeClass
// CHECK: integer_literal $Builtin.Int1, 0
// CHECK: store
%5 = alloc_stack $SomeClass
%6 = mark_uninitialized [var] %5 : $*SomeClass
cond_br %0, bb1, bb2
bb1:
// CHECK: = function_ref
%2 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
// CHECK-NEXT: [[V1:%[0-9]+]] = apply
%12 = apply %2() : $@convention(thin) () -> @owned SomeClass
// This assign becomes a simple store, since it is an initialize. This updates
// the control variable to say that it is initialized.
// CHECK: integer_literal $Builtin.Int1, -1
// CHECK: store {{.*}} to [trivial] [[CONTROL]]
// CHECK: store [[V1]] to [init] [[CLASSVAL]]
assign %12 to %6 : $*SomeClass // id: %13
br bb2 // id: %14
bb2:
// CHECK: = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
%3 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
// CHECK-NEXT: [[V2:%[0-9]+]] = apply
%17 = apply %3() : $@convention(thin) () -> @owned SomeClass
// This assign is either an initialize or an assign depending on whether %0 is
// true or not. This gets expanded out to a conditional destroy of the value.
// CHECK: load [trivial] [[CONTROL]]
// CHECK: cond_br
// CHECK: bb3:
// CHECK: destroy_addr [[CLASSVAL]]
// CHECK: br bb4
// CHECK: bb4:
// CHECK: store [[V2]] to [init] [[CLASSVAL]]
assign %17 to %6 : $*SomeClass // id: %18
destroy_addr %6 : $*SomeClass // id: %19
dealloc_stack %5 : $*SomeClass // id: %20
%23 = tuple () // user: %24
return %23 : $() // id: %24
}