Files
Joe Groff 5606207912 SIL: Add an "unresolved" flag to BeginApply and BeginAccess instructions.
We will have SILGen mark accesses as unresolved when lifetime resolution
needs to run in order to establish the final point where the access
ends.
2026-10-05 08:55:36 -07:00

314 lines
13 KiB
Plaintext

// First parse this and then emit a *.sib. Then read in the *.sib, then recreate
// RUN: %empty-directory(%t)
// RUN: %target-sil-opt -sil-print-types %s -emit-sib -o %t/tmp.sib -module-name borrow
// RUN: %target-sil-opt -sil-print-types %t/tmp.sib -o %t/tmp.2.sib -module-name borrow
// RUN: %target-sil-opt -sil-print-types %t/tmp.2.sib -module-name borrow -emit-sorted-sil | %FileCheck %s
import Builtin
import Swift
class TestArrayStorage {
@_hasStorage var count: Int32
init()
}
class C {}
struct TestArray2 {
var storage : TestArrayStorage
var someValue : Int32
var storage2 : TestArrayStorage
}
struct Int32 {
var x: Builtin.Int32
}
struct EmptyStruct {}
struct MoveOnlyStruct: ~Copyable {
@_hasStorage var i: Int32
deinit
}
// CHECK-LABEL: sil @alloc_ref_non_nested : $@convention(thin) () -> ()
sil @alloc_ref_non_nested : $@convention(thin) () -> () {
bb0:
// CHECK: %0 = alloc_ref [stack] [non_nested] $C
%0 = alloc_ref [stack] [non_nested] $C
dealloc_stack_ref %0
%2 = metatype $@thick C.Type
// CHECK: %3 = alloc_ref_dynamic [stack] [non_nested] %2 : $@thick C.Type, $C
%3 = alloc_ref_dynamic [stack] [non_nested] %2 : $@thick C.Type, $C
dealloc_stack_ref %3
%result = tuple ()
return %result
}
// CHECK-LABEL: sil @async_test : $@convention(thin) @async
sil @async_test : $@async () -> () {
bb0:
%0 = tuple ()
return %0 : $()
}
// CHECK-LABEL: sil @end_formal_scope :
sil @end_formal_scope : $@convention(thin) (Builtin.Int32) -> () {
// CHECK: bb0([[ARG:%[0-9]+]] :
entry(%arg : $Builtin.Int32):
// CHECK: [[LOCAL_LET:%.*]] = integer_literal
%local_let = integer_literal $Builtin.Int32, 1
// CHECK: [[LOCAL_VAR:%.*]] = alloc_stack
%local_var = alloc_stack $Builtin.Int32
// CHECK: end_formal_scope [[LOCAL_VAR]]
end_formal_scope %local_var
dealloc_stack %local_var
// CHECK: end_formal_scope [[LOCAL_LET]]
end_formal_scope %local_let
// CHECK: end_formal_scope [[ARG]]
end_formal_scope %arg
return undef : $()
}
// CHECK: sil [ownership_for_trivial] [ossa] @ownership_for_trivial
sil [ossa] [ownership_for_trivial] @ownership_for_trivial : $@convention(thin) (Int) -> () {
entry(%0 : @owned $Int):
destroy_value %0
return undef : $()
}
sil [ossa] @strong_copy_unmanaged_value_test : $@convention(thin) (@sil_unmanaged Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : $@sil_unmanaged Builtin.NativeObject):
%1 = strong_copy_unmanaged_value %0 : $@sil_unmanaged Builtin.NativeObject
return %1 : $Builtin.NativeObject
}
class A {
@_hasStorage var property: Any { get set }
deinit
init()
}
// CHECK-LABEL: sil @test_access : $@convention(thin) (@guaranteed A) -> () {
// CHECK: begin_access [read] [dynamic] [unresolved]
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
// CHECK: begin_unpaired_access [read] [dynamic]
// CHECK: begin_unpaired_access [read] [dynamic] [no_nested_conflict]
// CHECK-LABEL: } // end sil function 'test_access'
sil @test_access : $(@guaranteed A) -> () {
bb0(%0 : $A):
%1 = alloc_stack $Any
%2 = ref_element_addr %0 : $A, #A.property
%3 = begin_access [unresolved] [dynamic] [read] %2 : $*Any
copy_addr %3 to [init] %1 : $*Any
end_access %3 : $*Any
%6 = begin_access [read] [dynamic] [no_nested_conflict] %2 : $*Any
copy_addr %6 to %1 : $*Any
end_access %6 : $*Any
%9 = alloc_stack $Builtin.UnsafeValueBuffer
begin_unpaired_access [read] [dynamic] %2 : $*Any, %9 : $*Builtin.UnsafeValueBuffer
copy_addr %2 to %1 : $*Any
end_unpaired_access [dynamic] %9 : $*Builtin.UnsafeValueBuffer
begin_unpaired_access [read] [dynamic] [no_nested_conflict] %2 : $*Any, %9 : $*Builtin.UnsafeValueBuffer
copy_addr %2 to %1 : $*Any
destroy_addr %1 : $*Any
dealloc_stack %9 : $*Builtin.UnsafeValueBuffer
dealloc_stack %1 : $*Any
%20 = tuple ()
return %20 : $()
}
sil [ossa] @test_destructure_struct_tuple : $@convention(thin) (@owned (Builtin.NativeObject, Builtin.Int32), @owned TestArray2) -> @owned (Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage) {
bb0(%0 : @owned $(Builtin.NativeObject, Builtin.Int32), %1 : @owned $TestArray2):
(%2, %3) = destructure_tuple %0 : $(Builtin.NativeObject, Builtin.Int32)
(%4, %5, %6) = destructure_struct %1 : $TestArray2
%7 = tuple(%2 : $Builtin.NativeObject, %3 : $Builtin.Int32, %4 : $TestArrayStorage, %5 : $Int32, %6 : $TestArrayStorage)
return %7 : $(Builtin.NativeObject, Builtin.Int32, TestArrayStorage, Int32, TestArrayStorage)
}
// CHECK-LABEL: sil [ossa] @test_drop_deinit :
// CHECK: %1 = drop_deinit %0 : $MoveOnlyStruct
// CHECK-LABEL: } // end sil function 'test_drop_deinit'
sil [ossa] @test_drop_deinit : $@convention(thin) (@owned MoveOnlyStruct) -> () {
bb0(%0 : @owned $MoveOnlyStruct):
%1 = drop_deinit %0 : $MoveOnlyStruct
destroy_value %1 : $MoveOnlyStruct
%3 = tuple ()
return %3 : $()
}
sil @test_empty_destructure : $@convention(thin) () -> () {
bb0:
%0 = struct $EmptyStruct()
() = destructure_struct %0 : $EmptyStruct
%1 = tuple()
() = destructure_tuple %1 : $()
return %1 : $()
}
// CHECK-LABEL: sil [serialized] [ossa] @test_end_lifetime : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: end_lifetime {{%.*}} : $Builtin.NativeObject
sil [serialized] [ossa] @test_end_lifetime : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : @owned $Builtin.NativeObject):
end_lifetime %0 : $Builtin.NativeObject
return undef : $()
}
// CHECK-LABEL: sil [lazy_getter] @test_lazy_getter
sil [lazy_getter] @test_lazy_getter : $@convention(thin) () -> () {
bb0:
%0 = tuple ()
return %0 : $()
}
// CHECK-LABEL: sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
// CHECK: bb0([[REGISTER_0:%[^,]+]] :
// CHECK-NEXT: [[REGISTER_1:%[^,]+]] = move_value [[REGISTER_0]]
// CHECK-NEXT: [[REGISTER_2:%[^,]+]] = move_value [allows_diagnostics] [[REGISTER_1]]
// CHECK-NEXT: [[REGISTER_3:%[^,]+]] = move_value [lexical] [[REGISTER_2]]
// CHECK-NEXT: [[REGISTER_4:%[^,]+]] = move_value [allows_diagnostics] [lexical] [[REGISTER_3]]
// CHECK-NEXT: [[REGISTER_5:%[^,]+]] = move_value [allows_diagnostics] [lexical] [[REGISTER_4]]
// CHECK-NEXT: [[REGISTER_6:%[^,]+]] = move_value [var_decl] [[REGISTER_5]]
// CHECK-NEXT: return [[REGISTER_6]]
// CHECK-NEXT: } // end sil function 'test_movevalue_parsing'
sil [ossa] @test_movevalue_parsing : $@convention(thin) (@owned Builtin.NativeObject) -> @owned Builtin.NativeObject {
bb0(%0 : @owned $Builtin.NativeObject):
%1 = move_value %0 : $Builtin.NativeObject
%2 = move_value [allows_diagnostics] %1 : $Builtin.NativeObject
%3 = move_value [lexical] %2 : $Builtin.NativeObject
%4 = move_value [allows_diagnostics] [lexical] %3 : $Builtin.NativeObject
%5 = move_value [lexical] [allows_diagnostics] %4 : $Builtin.NativeObject
%6 = move_value [var_decl] %5 : $Builtin.NativeObject
return %6 : $Builtin.NativeObject
}
// CHECK-LABEL: sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () {
sil [no_allocation] [ossa] @test_no_allocation : $@convention(thin) () -> () {
bb0:
%1 = tuple ()
return %1 : $()
}
// CHECK-LABEL: sil [no_locks] [ossa] @test_no_locks : $@convention(thin) () -> () {
sil [no_locks] [ossa] @test_no_locks : $@convention(thin) () -> () {
bb0:
%1 = tuple ()
return %1 : $()
}
// CHECK-LABEL: sil [ossa] @test_pointer_to_address : $@convention(thin) (Builtin.RawPointer, Builtin.Int64) -> () {
// CHECK: pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Builtin.Int64
// CHECK: pointer_to_address %0 : $Builtin.RawPointer to [invariant] $*Builtin.Int64
// CHECK: pointer_to_address %0 : $Builtin.RawPointer to [align=1] $*Builtin.Int64
// CHECK: pointer_to_address %0 : $Builtin.RawPointer to [strict] [align=8] $*Builtin.Int64
// CHECK: pointer_to_address %0 : $Builtin.RawPointer to [align=4294967296] $*Builtin.Int64
sil [ossa] @test_pointer_to_address : $@convention(thin) (Builtin.RawPointer, Builtin.Int64) -> () {
bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Int64):
%3 = pointer_to_address %0 : $Builtin.RawPointer to $*Builtin.Int64
store %1 to [trivial] %3 : $*Builtin.Int64
%5 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*Builtin.Int64
store %1 to [trivial] %5 : $*Builtin.Int64
%7 = pointer_to_address %0 : $Builtin.RawPointer to [invariant] $*Builtin.Int64
store %1 to [trivial] %7 : $*Builtin.Int64
%9 = pointer_to_address %0 : $Builtin.RawPointer to [align=1] $*Builtin.Int64
store %1 to [trivial] %9 : $*Builtin.Int64
%11 = pointer_to_address %0 : $Builtin.RawPointer to [strict] [align=8] $*Builtin.Int64
store %1 to [trivial] %11 : $*Builtin.Int64
%13 = pointer_to_address %0 : $Builtin.RawPointer to [align=4294967296] $*Builtin.Int64
store %1 to [trivial] %13 : $*Builtin.Int64
%28 = tuple ()
return %28 : $()
}
// CHECK-LABEL: sil [ossa] @test_raw_pointer_to_ref : $@convention(thin) (Builtin.RawPointer) -> () {
// CHECK: [[IMMORTAL:%.*]] = raw_pointer_to_ref [immortal] %0 : $Builtin.RawPointer to $Builtin.NativeObject
// CHECK: fix_lifetime [[IMMORTAL]] : $Builtin.NativeObject
// CHECK: [[OWNED:%.*]] = raw_pointer_to_ref %0 : $Builtin.RawPointer to $Builtin.NativeObject
// CHECK: destroy_value [[OWNED]] : $Builtin.NativeObject
// CHECK-LABEL: } // end sil function 'test_raw_pointer_to_ref'
sil [ossa] @test_raw_pointer_to_ref : $@convention(thin) (Builtin.RawPointer) -> () {
bb0(%0 : $Builtin.RawPointer):
// An immortal result has "none" ownership, so it must not be destroyed.
%1 = raw_pointer_to_ref [immortal] %0 : $Builtin.RawPointer to $Builtin.NativeObject
fix_lifetime %1 : $Builtin.NativeObject
// A non-immortal result is "owned", so it has to be destroyed.
%3 = raw_pointer_to_ref %0 : $Builtin.RawPointer to $Builtin.NativeObject
destroy_value %3 : $Builtin.NativeObject
%5 = tuple ()
return %5 : $()
}
// CHECK-LABEL: sil [serialized] [ossa] @test_subst_function_type : $@convention(thin) (@guaranteed @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () for <Builtin.Int32, Builtin.Int64>) -> ()
sil [serialized] [ossa] @test_subst_function_type : $@convention(thin) (@guaranteed @callee_guaranteed @substituted <A, B> (@in A, @in B) -> () for <Builtin.Int32, Builtin.Int64>) -> () {
entry(%0 : @guaranteed $@callee_guaranteed @substituted <C, D> (@in C, @in D) -> () for <Builtin.Int32, Builtin.Int64>):
return undef : $()
}
// CHECK-LABEL: sil [serialized] [ossa] @test_subst_function_type_generic_context : $@convention(thin) <X, Y> (@guaranteed @callee_guaranteed @substituted <τ_0_0, τ_0_1> (@in τ_0_0, @in τ_0_1) -> () for <X, Y>) -> ()
sil [serialized] [ossa] @test_subst_function_type_generic_context : $@convention(thin) <X, Y> (@guaranteed @callee_guaranteed @substituted <A, B> (@in A, @in B) -> () for <X, Y>) -> () {
entry(%0 : @guaranteed $@callee_guaranteed @substituted <C, D> (@in C, @in D) -> () for <X, Y>):
return undef : $()
}
// CHECK-LABEL: sil [serialized] [ossa] @test_unchecked_ownership_conversion : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
// CHECK: unchecked_ownership_conversion {{%.*}} : $Builtin.NativeObject, @guaranteed to @owned
sil [serialized] [ossa] @test_unchecked_ownership_conversion : $@convention(thin) (@guaranteed Builtin.NativeObject) -> () {
bb0(%0 : @guaranteed $Builtin.NativeObject):
%1 = unchecked_ownership_conversion %0 : $Builtin.NativeObject, @guaranteed to @owned
destroy_value %1 : $Builtin.NativeObject
return undef : $()
}
// CHECK-LABEL: sil @throwError : $@convention(thin) <τ_0_0, τ_0_1> (Builtin.Int64, @in τ_0_0) -> (@out τ_0_1, @error_indirect τ_0_0)
// CHECK: copy_addr [take] %3 to [init] %1 : $*τ_0_0
// CHECK: throw %1 : $*τ_0_0
sil @throwError : $@convention(thin) <τ_0_0, τ_0_1> (Builtin.Int64, @in τ_0_0) -> (@error_indirect τ_0_0, @out τ_0_1) {
bb0(%0: $*τ_0_1, %1: $*τ_0_0, %2 : $Builtin.Int64, %3 : $*τ_0_0):
copy_addr [take] %3 to [init] %1 : $*τ_0_0
throw %1 : $*τ_0_0
}
// CHECK: sil @throwError_unowned : $@convention(thin) (Builtin.Int64) -> @error_unowned Builtin.Int64 {
// CHECK: bb0(%0 : $Builtin.Int64):
// CHECK: throw %0 : $Builtin.Int64
sil @throwError_unowned : $@convention(thin) (Builtin.Int64) -> @error_unowned Builtin.Int64 {
bb0(%0 : $Builtin.Int64):
throw %0 : $Builtin.Int64
}
// CHECK: sil @try_apply : $@convention(thin) <T, V> (@in T) -> @out V
// CHECK: bb0(%0 : $*V, %1 : $*T):
// CHECK: %2 = alloc_stack $T
// CHECK: %3 = integer_literal $Builtin.Int64, 0
// CHECK: %4 = function_ref @throwError : $@convention(thin) <τ_0_0, τ_0_1> (Builtin.Int64, @in τ_0_0) -> (@out τ_0_1, @error_indirect τ_0_0)
// CHECK: try_apply %4<T, V>(%0, %2, %3, %1) : $@convention(thin) <τ_0_0, τ_0_1> (Builtin.Int64, @in τ_0_0) -> (@out τ_0_1, @error_indirect τ_0_0), normal bb2, error bb1
// CHECK: bb1:
// CHECK: destroy_addr %2 : $*T
// CHECK: br bb3
// CHECK: bb2(%8 : $()):
// CHECK: br bb3
sil @try_apply : $@convention(thin) <T, V> (@in T) -> @out V {
bb0(%0 : $*V, %1: $*T):
%2 = alloc_stack $T
%3 = integer_literal $Builtin.Int64, 0
%4 = function_ref @throwError : $@convention(thin) <T, V> (Builtin.Int64, @in T) -> (@error_indirect T, @out V)
try_apply %4<T, V>(%0, %2, %3, %1) : $@convention(thin) <T, V> (Builtin.Int64, @in T) -> (@error_indirect T, @out V), normal bb1, error bb2
bb1(%5 : $()):
br bb3
bb2:
destroy_addr %2 : $*T
br bb3
bb3:
dealloc_stack %2 : $*T
%6 = tuple ()
return %6 : $()
}