| // RUN: %target-sil-opt -enable-sil-ownership -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 -o /dev/null 2>&1 %s | %FileCheck %s |
| // RUN: %target-sil-opt -enable-sil-ownership -sil-ownership-verifier-enable-testing -enable-sil-verify-all=0 -o /dev/null 2>&1 %s | %FileCheck -check-prefix=NEGATIVE-TEST %s |
| // REQUIRES: asserts |
| |
| sil_stage canonical |
| |
| import Builtin |
| |
| ////////////////// |
| // Declarations // |
| ////////////////// |
| |
| enum Optional<T> { |
| case some(T) |
| case none |
| } |
| |
| struct A { |
| var ptr: Builtin.NativeObject |
| var ptr2: Optional<(Builtin.NativeObject, Builtin.NativeObject)> |
| } |
| |
| struct B { |
| var a1: A |
| var a2: A |
| } |
| |
| sil @owned_use_of_nativeobject : $@convention(thin) (@owned Builtin.NativeObject) -> () |
| sil @guaranteed_use_of_nativeobject : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () |
| |
| /////////// |
| // Tests // |
| /////////// |
| |
| //===--- |
| // begin_borrow subobject tests |
| // |
| |
| // NEGATIVE-TEST-NOT: Function: 'value_subobject_without_corresponding_end_borrow' |
| sil @value_subobject_without_corresponding_end_borrow : $@convention(thin) (@owned B) -> () { |
| bb0(%0 : @owned $B): |
| %1 = begin_borrow %0 : $B |
| %2 = struct_extract %1 : $B, #B.a1 |
| %3 = struct_extract %2 : $A, #A.ptr |
| %4 = function_ref @guaranteed_use_of_nativeobject : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () |
| apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () |
| end_borrow %1 from %0 : $B, $B |
| destroy_value %0 : $B |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We fail here since we have a use of our subobject after the end_borrow which |
| // ends the borrow's lifetime. |
| // |
| // CHECK-LABEL: Function: 'value_subobject_with_use_after_end_borrow' |
| // CHECK: Found use after free?! |
| // CHECK: Value: %1 = begin_borrow %0 : $B |
| // CHECK: Consuming User: end_borrow %1 from %0 : $B, $B |
| // CHECK: Non Consuming User: %4 = struct_extract %2 : $A, #A.ptr |
| // CHECK: Block: bb0 |
| sil @value_subobject_with_use_after_end_borrow : $@convention(thin) (@owned B) -> () { |
| bb0(%0 : @owned $B): |
| %1 = begin_borrow %0 : $B |
| %2 = struct_extract %1 : $B, #B.a1 |
| end_borrow %1 from %0 : $B, $B |
| %3 = struct_extract %2 : $A, #A.ptr |
| destroy_value %0 : $B |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We fail here since we have a destroy of our subobject. The idea is to make |
| // sure that our special handling code for subobjects here does not stop us from |
| // detecting violations of ownership kinds on the subobjects. |
| // |
| // CHECK-LABEL: Function: 'value_subobject_with_destroy_of_subobject' |
| // CHECK: Have operand with incompatible ownership?! |
| // CHECK: Value: %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 |
| // CHECK: BaseValue: %1 = begin_borrow %0 : $B |
| // CHECK: User: destroy_value %5 : $Builtin.NativeObject |
| // CHECK: Conv: guaranteed |
| |
| // Make sure we only see one failure here. We should process recursively from |
| // the borrow root, not from each of the subobjects of the borrow. |
| // NEGATIVE-TEST: Function: 'value_subobject_with_destroy_of_subobject' |
| // NEGATIVE-TEST-NOT: Function: 'value_subobject_with_destroy_of_subobject' |
| sil @value_subobject_with_destroy_of_subobject : $@convention(thin) (@owned B) -> () { |
| bb0(%0 : @owned $B): |
| %1 = begin_borrow %0 : $B |
| %2 = struct_extract %1 : $B, #B.a1 |
| %3 = struct_extract %2 : $A, #A.ptr2 |
| %4 = unchecked_enum_data %3 : $Optional<(Builtin.NativeObject, Builtin.NativeObject)>, #Optional.some!enumelt.1 |
| %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 |
| destroy_value %5 : $Builtin.NativeObject |
| end_borrow %1 from %0 : $B, $B |
| destroy_value %0 : $B |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // CHECK-LABEL: Function: 'value_different_subobject_kinds_multiple_levels' |
| // CHECK: Found use after free?! |
| // CHECK: Value: %1 = begin_borrow %0 : $B |
| // CHECK: Consuming User: end_borrow %1 from %0 : $B, $B |
| // CHECK: Non Consuming User: %6 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 |
| // CHECK: Block: bb0 |
| sil @value_different_subobject_kinds_multiple_levels : $@convention(thin) (@owned B) -> () { |
| bb0(%0 : @owned $B): |
| %1 = begin_borrow %0 : $B |
| %2 = struct_extract %1 : $B, #B.a1 |
| %3 = struct_extract %2 : $A, #A.ptr2 |
| %4 = unchecked_enum_data %3 : $Optional<(Builtin.NativeObject, Builtin.NativeObject)>, #Optional.some!enumelt.1 |
| end_borrow %1 from %0 : $B, $B |
| %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 |
| destroy_value %0 : $B |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| //===--- |
| // Function Argument guaranteed tests |
| // |
| |
| // NEGATIVE-TEST-NOT: Function: 'funcarg_subobject_basic_test' |
| sil @funcarg_subobject_basic_test : $@convention(thin) (@guaranteed B) -> () { |
| bb0(%0 : @guaranteed $B): |
| %2 = struct_extract %0 : $B, #B.a1 |
| %3 = struct_extract %2 : $A, #A.ptr |
| %4 = function_ref @guaranteed_use_of_nativeobject : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () |
| apply %4(%3) : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () |
| %9999 = tuple() |
| return %9999 : $() |
| } |
| |
| // We fail here since we have a destroy of our subobject. The idea is to make |
| // sure that our special handling code for subobjects here does not stop us from |
| // detecting violations of ownership kinds on the subobjects. |
| // |
| // CHECK-LABEL: Function: 'funcarg_subobject_with_destroy_of_subobject' |
| // CHECK: Have operand with incompatible ownership?! |
| // CHECK: Value: %4 = tuple_extract %3 : $(Builtin.NativeObject, Builtin.NativeObject), 1 |
| // CHECK: BaseValue: %0 = argument of bb0 : $B |
| // CHECK: User: destroy_value %4 : $Builtin.NativeObject |
| // CHECK: Conv: guaranteed |
| sil @funcarg_subobject_with_destroy_of_subobject : $@convention(thin) (@guaranteed B) -> () { |
| bb0(%0 : @guaranteed $B): |
| %2 = struct_extract %0 : $B, #B.a1 |
| %3 = struct_extract %2 : $A, #A.ptr2 |
| %4 = unchecked_enum_data %3 : $Optional<(Builtin.NativeObject, Builtin.NativeObject)>, #Optional.some!enumelt.1 |
| %5 = tuple_extract %4 : $(Builtin.NativeObject, Builtin.NativeObject), 1 |
| destroy_value %5 : $Builtin.NativeObject |
| %9999 = tuple() |
| return %9999 : $() |
| } |