mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Textual SIL was sometimes ambiguous when SILDeclRefs were used, because the textual representation of SILDeclRefs was the same for functions that have the same name, but different signatures.
309 lines
14 KiB
Plaintext
309 lines
14 KiB
Plaintext
// RUN: %target-sil-opt -assume-parsing-unqualified-ownership-sil -enable-sil-verify-all %s -capture-promotion | %FileCheck %s
|
|
|
|
// Check to make sure that the process of promoting closure captures results in
|
|
// a correctly cloned and modified closure function body. This test
|
|
// intentionally only includes one promotable closure so that there is minimal
|
|
// ordering dependence in the checked output.
|
|
|
|
sil_stage raw
|
|
|
|
import Builtin
|
|
|
|
struct Int {
|
|
var value : Builtin.Int64
|
|
}
|
|
|
|
class Foo {
|
|
func foo() -> Int
|
|
}
|
|
|
|
class Bar {
|
|
}
|
|
|
|
struct Baz {
|
|
var bar: Bar
|
|
var x: Int
|
|
}
|
|
|
|
sil @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
|
|
sil @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
sil @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
|
|
sil @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
|
|
|
|
// CHECK-LABEL: sil @test_capture_promotion
|
|
sil @test_capture_promotion : $@convention(thin) () -> @owned @callee_owned () -> Int {
|
|
bb0:
|
|
%0 = tuple ()
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Foo>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
|
|
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
%3 = metatype $@thick Foo.Type
|
|
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
store %4 to %1a : $*Foo
|
|
%6 = alloc_box $<τ_0_0> { var τ_0_0 } <Baz>
|
|
%6a = project_box %6 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
|
|
%7 = function_ref @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
|
|
%8 = metatype $@thin Baz.Type
|
|
%9 = apply %7(%8) : $@convention(thin) (@thin Baz.Type) -> @owned Baz
|
|
store %9 to %6a : $*Baz
|
|
%11 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%11a = project_box %11 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%12 = function_ref @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
|
|
%13 = metatype $@thin Int.Type
|
|
%14 = integer_literal $Builtin.Word, 3
|
|
%15 = apply %12(%14, %13) : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
|
|
store %15 to %11a : $*Int
|
|
|
|
// CHECK-NOT: function_ref @closure0 :
|
|
// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @_TTSf2i_i_i__closure0
|
|
// CHECK-NOT: function_ref @closure0 :
|
|
|
|
// The three strong retains are removed
|
|
|
|
// The Foo variable is loaded from and retained, because it is a reference type
|
|
// CHECK-NEXT: [[LOADFOO:%.*]] = load {{.*}} : $*Foo
|
|
// CHECK-NEXT: strong_retain [[LOADFOO]] : $Foo
|
|
|
|
// The Baz variable is loaded and retain_value'd, because it is a non-trivial
|
|
// aggregate type
|
|
// CHECK-NEXT: [[LOADBAZ:%.*]] = load {{.*}} : $*Baz
|
|
// CHECK-NEXT: retain_value [[LOADBAZ:%.*]] : $Baz
|
|
|
|
// The Int variable is loaded only, because it is trivial
|
|
// CHECK-NEXT: [[LOADINT:%.*]] = load {{.*}} : $*Int
|
|
|
|
// The partial apply has one value argument for each pair of arguments that was
|
|
// previously used to capture and pass the variable by reference
|
|
// CHECK-NEXT: {{.*}} = partial_apply [[CLOSURE_PROMOTE]]([[LOADFOO]], [[LOADBAZ]], [[LOADINT]])
|
|
|
|
%17 = function_ref @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> Int
|
|
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
strong_retain %6 : $<τ_0_0> { var τ_0_0 } <Baz>
|
|
strong_retain %11 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%21 = partial_apply %17(%1, %6, %11) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> Int
|
|
|
|
strong_release %11 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Baz>
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
|
|
return %21 : $@callee_owned () -> Int
|
|
}
|
|
|
|
// CHECK-LABEL: sil @test_capture_promotion_indirect
|
|
sil @test_capture_promotion_indirect : $@convention(thin) () -> @owned @callee_owned () -> @out Int {
|
|
bb0:
|
|
%0 = tuple ()
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Foo>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
|
|
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
%3 = metatype $@thick Foo.Type
|
|
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
store %4 to %1a : $*Foo
|
|
%6 = alloc_box $<τ_0_0> { var τ_0_0 } <Baz>
|
|
%6a = project_box %6 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
|
|
%7 = function_ref @baz_init : $@convention(thin) (@thin Baz.Type) -> @owned Baz
|
|
%8 = metatype $@thin Baz.Type
|
|
%9 = apply %7(%8) : $@convention(thin) (@thin Baz.Type) -> @owned Baz
|
|
store %9 to %6a : $*Baz
|
|
%11 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%11a = project_box %11 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%12 = function_ref @convert_from_integer_literal : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
|
|
%13 = metatype $@thin Int.Type
|
|
%14 = integer_literal $Builtin.Word, 3
|
|
%15 = apply %12(%14, %13) : $@convention(thin) (Builtin.Word, @thin Int.Type) -> Int
|
|
store %15 to %11a : $*Int
|
|
|
|
// CHECK-NOT: function_ref @closure_indirect_result :
|
|
// CHECK: [[CLOSURE_PROMOTE:%.*]] = function_ref @_TTSf2n_i_i_i__closure_indirect_result
|
|
// CHECK-NOT: function_ref @closure_indirect_result :
|
|
|
|
// The three strong retains are removed
|
|
|
|
// The Foo variable is loaded from and retained, because it is a reference type
|
|
// CHECK-NEXT: [[LOADFOO:%.*]] = load {{.*}} : $*Foo
|
|
// CHECK-NEXT: strong_retain [[LOADFOO]] : $Foo
|
|
|
|
// The Baz variable is loaded and retain_value'd, because it is a non-trivial
|
|
// aggregate type
|
|
// CHECK-NEXT: [[LOADBAZ:%.*]] = load {{.*}} : $*Baz
|
|
// CHECK-NEXT: retain_value [[LOADBAZ:%.*]] : $Baz
|
|
|
|
// The Int variable is loaded only, because it is trivial
|
|
// CHECK-NEXT: [[LOADINT:%.*]] = load {{.*}} : $*Int
|
|
|
|
// The partial apply has one value argument for each pair of arguments that was
|
|
// previously used to capture and pass the variable by reference
|
|
// CHECK-NEXT: {{.*}} = partial_apply [[CLOSURE_PROMOTE]]([[LOADFOO]], [[LOADBAZ]], [[LOADINT]])
|
|
|
|
|
|
%17 = function_ref @closure_indirect_result : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> @out Int
|
|
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
strong_retain %6 : $<τ_0_0> { var τ_0_0 } <Baz>
|
|
strong_retain %11 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%21 = partial_apply %17(%1, %6, %11) : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> @out Int
|
|
|
|
strong_release %11 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_release %6 : $<τ_0_0> { var τ_0_0 } <Baz>
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
|
|
return %21 : $@callee_owned () -> @out Int
|
|
}
|
|
|
|
// CHECK-LABEL: sil private @_TTSf2i_i_i__closure0 : $@convention(thin) (@owned Foo, @owned Baz, Int) -> Int
|
|
// CHECK: [[DUMMY_FUNC:%.*]] = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
|
|
|
|
// The load of %1 is removed, and its uses replaced with the Foo argument
|
|
// CHECK-NEXT: strong_retain {{.*}} : $Foo
|
|
// CHECK-NEXT: [[METHOD_FOO:%.*]] = class_method {{.*}} : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
|
|
// CHECK-NEXT: [[APPLY_FOO:%.*]] = apply [[METHOD_FOO]]({{.*}}) : $@convention(method) (@guaranteed Foo) -> Int
|
|
|
|
// The struct_element_addr of %3 followed by a load is replaced by a struct_extract of the Baz argument
|
|
// CHECK-NEXT: [[EXTRACT_BAZ_X:%.*]] = struct_extract {{.*}} : $Baz, #Baz.x
|
|
|
|
// CHECK-NEXT: [[RETVAL:%.*]] = apply [[DUMMY_FUNC]]([[APPLY_FOO]], [[EXTRACT_BAZ_X]], {{.*}}) : $@convention(thin) (Int, Int, Int) -> Int
|
|
|
|
// The release of %4 is removed because the Int type is trivial
|
|
|
|
// The release of %2 is replaced by a release_value of the Baz argument, since
|
|
// it is a non-trivial aggregate
|
|
// CHECK-NEXT: release_value {{.*}} : $Baz
|
|
|
|
// The release of %0 is replaced by a strong_release of the Foo argument, since
|
|
// it is a reference type
|
|
// CHECK-NEXT: strong_release {{.*}} : $Foo
|
|
|
|
// CHECK-NEXT: return [[RETVAL]] : $Int
|
|
|
|
sil private @closure0 : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> Int {
|
|
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Foo>, %2 : $<τ_0_0> { var τ_0_0 } <Baz>, %4 : $<τ_0_0> { var τ_0_0 } <Int>):
|
|
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
|
|
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
|
|
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%6 = tuple ()
|
|
// function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int
|
|
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
|
|
%8 = load %1 : $*Foo
|
|
strong_retain %8 : $Foo
|
|
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
|
|
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
|
|
%12 = struct_element_addr %3 : $*Baz, #Baz.x
|
|
%13 = load %12 : $*Int
|
|
%14 = load %5 : $*Int
|
|
%15 = apply %7(%11, %13, %14) : $@convention(thin) (Int, Int, Int) -> Int
|
|
strong_release %4 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Baz>
|
|
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
return %15 : $Int
|
|
}
|
|
|
|
// The closure in this function is not promotable because it mutates its argument
|
|
|
|
// CHECK-LABEL: sil @test_unpromotable
|
|
sil @test_unpromotable : $@convention(thin) () -> @owned @callee_owned () -> Int {
|
|
bb0:
|
|
%0 = tuple ()
|
|
%1 = alloc_box $<τ_0_0> { var τ_0_0 } <Foo>
|
|
%1a = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
|
|
%2 = function_ref @foo_allocating_init : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
%3 = metatype $@thick Foo.Type
|
|
%4 = apply %2(%3) : $@convention(thin) (@thick Foo.Type) -> @owned Foo
|
|
store %4 to %1a : $*Foo
|
|
%17 = function_ref @closure1 : $@convention(thin) (<τ_0_0> { var τ_0_0 } <Foo>) -> Int
|
|
strong_retain %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
// CHECK: partial_apply {{%.*}}({{%.*}})
|
|
%21 = partial_apply %17(%1) : $@convention(thin) (<τ_0_0> { var τ_0_0 } <Foo>) -> Int
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
return %21 : $@callee_owned () -> Int
|
|
}
|
|
|
|
sil @mutate_foo : $@convention(thin) (@inout Foo) -> ()
|
|
|
|
sil private @closure1 : $@convention(thin) (<τ_0_0> { var τ_0_0 } <Foo>) -> Int {
|
|
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Foo>):
|
|
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
|
|
%6 = tuple ()
|
|
// function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int
|
|
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
|
|
%8 = load %1 : $*Foo
|
|
strong_retain %8 : $Foo
|
|
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
|
|
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
|
|
%12 = function_ref @mutate_foo : $@convention(thin) (@inout Foo) -> ()
|
|
%13 = apply %12(%1) : $@convention(thin) (@inout Foo) -> ()
|
|
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
return %11 : $Int
|
|
}
|
|
|
|
sil @apply : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
|
|
|
|
// CHECK-LABEL: sil @captureWithinGeneric
|
|
sil @captureWithinGeneric : $@convention(thin) <T> (@inout Int, @inout Int) -> () {
|
|
// CHECK: bb0
|
|
bb0(%0 : $*Int, %1 : $*Int):
|
|
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
copy_addr %0 to [initialization] %2a : $*Int
|
|
%4 = alloc_box $<τ_0_0> { var τ_0_0 } <Int>
|
|
%4a = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
copy_addr %1 to [initialization] %4a : $*Int
|
|
%6 = function_ref @apply : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
|
|
// CHECK: [[PROMOTED:%[0-9a-zA-Z]+]] = function_ref @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <Int>, Int) -> ()
|
|
%7 = function_ref @closureWithGenericSignature : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <Int>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
|
|
strong_retain %4 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_retain %2 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
// CHECK: partial_apply [[PROMOTED]]<{{[^>]+}}>(
|
|
%10 = partial_apply %7<T>(%4, %2) : $@convention(thin) <τ_0_0> (@owned <τ_0_0> { var τ_0_0 } <Int>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> ()
|
|
%11 = apply %6(%10) : $@convention(thin) (@owned @callee_owned () -> ()) -> ()
|
|
copy_addr %4a to %1 : $*Int
|
|
strong_release %4 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
copy_addr %2a to %0 : $*Int
|
|
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%16 = tuple ()
|
|
return %16 : $()
|
|
}
|
|
|
|
// CHECK: sil @_TTSf2n_i__closureWithGenericSignature : $@convention(thin) <{{[^>]+}}> (@owned <τ_0_0> { var τ_0_0 } <Int>, Int) -> ()
|
|
sil @closureWithGenericSignature : $@convention(thin) <T> (@owned <τ_0_0> { var τ_0_0 } <Int>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> () {
|
|
bb0(%0 : $<τ_0_0> { var τ_0_0 } <Int>, %2 : $<τ_0_0> { var τ_0_0 } <Int>):
|
|
%1 = project_box %0 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%4 = function_ref @_TFsoi1pFTSiSi_Si : $@convention(thin) (Int, Int) -> Int
|
|
%5 = load %3 : $*Int
|
|
%6 = load %3 : $*Int
|
|
%7 = apply %4(%5, %6) : $@convention(thin) (Int, Int) -> Int
|
|
assign %7 to %1 : $*Int
|
|
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_release %0 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
%11 = tuple ()
|
|
return %11 : $()
|
|
}
|
|
|
|
sil [transparent] [fragile] @_TFsoi1pFTSiSi_Si : $@convention(thin) (Int, Int) -> Int
|
|
|
|
|
|
sil private @closure_indirect_result : $@convention(thin) (@owned <τ_0_0> { var τ_0_0 } <Foo>, @owned <τ_0_0> { var τ_0_0 } <Baz>, @owned <τ_0_0> { var τ_0_0 } <Int>) -> @out Int {
|
|
bb0(%0: $*Int, %1 : $<τ_0_0> { var τ_0_0 } <Foo>, %2 : $<τ_0_0> { var τ_0_0 } <Baz>, %4 : $<τ_0_0> { var τ_0_0 } <Int>):
|
|
%17 = project_box %1 : $<τ_0_0> { var τ_0_0 } <Foo>, 0
|
|
%3 = project_box %2 : $<τ_0_0> { var τ_0_0 } <Baz>, 0
|
|
%5 = project_box %4 : $<τ_0_0> { var τ_0_0 } <Int>, 0
|
|
%6 = tuple ()
|
|
// function_ref test14.plus (a : Swift.Int, b : Swift.Int, c : Swift.Int) -> Swift.Int
|
|
%7 = function_ref @dummy_func : $@convention(thin) (Int, Int, Int) -> Int
|
|
%8 = load %17 : $*Foo
|
|
strong_retain %8 : $Foo
|
|
%10 = class_method %8 : $Foo, #Foo.foo!1 : (Foo) -> () -> Int, $@convention(method) (@guaranteed Foo) -> Int
|
|
%11 = apply %10(%8) : $@convention(method) (@guaranteed Foo) -> Int
|
|
%12 = struct_element_addr %3 : $*Baz, #Baz.x
|
|
%13 = load %12 : $*Int
|
|
%14 = load %5 : $*Int
|
|
%15 = apply %7(%11, %13, %14) : $@convention(thin) (Int, Int, Int) -> Int
|
|
strong_release %4 : $<τ_0_0> { var τ_0_0 } <Int>
|
|
strong_release %2 : $<τ_0_0> { var τ_0_0 } <Baz>
|
|
strong_release %1 : $<τ_0_0> { var τ_0_0 } <Foo>
|
|
store %15 to %0 : $*Int
|
|
%16 = tuple()
|
|
return %16 : $()
|
|
}
|
|
|
|
|