mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Instead of immediately creating closures for local function declarations and treating them directly as capturable values, break function captures down and transitively capture the storage necessary to invoke the captured functions. Change the way SILGen emits calls to closures and local functions so that it treats the capture list as the first curry level of an invocation, so that full applications of closure literals or nested functions don't require a partial apply at all. This allows references among local functions with captures to work within the existing confines of partial_apply, and also has the nice benefit that circular references would work without creating reference cycles (though Sema unfortunately rejects them; something we arguably ought to fix.)
This fixes rdar://problem/11266246 and improves codegen of local functions. Full applications of functions, or immediate applications of closure literals like { }(), now never need to allocate a closure.
Swift SVN r28112
538 lines
21 KiB
Swift
538 lines
21 KiB
Swift
// RUN: %target-swift-frontend -parse-stdlib -parse-as-library -emit-silgen %s | FileCheck %s
|
|
|
|
import Swift
|
|
|
|
var zero = 0
|
|
|
|
// <rdar://problem/15921334>
|
|
// CHECK-LABEL: sil hidden @_TF8closures46return_local_generic_function_without_capturesU___FT_FQ_Q0_ : $@convention(thin) <A, R> () -> @owned @callee_owned (@out R, @in A) -> () {
|
|
func return_local_generic_function_without_captures<A, R>() -> A -> R {
|
|
func f(_: A) -> R {
|
|
Builtin.int_trap()
|
|
}
|
|
// CHECK: [[FN:%.*]] = function_ref @_TFF8closures46return_local_generic_function_without_capturesU___FT_FQ_Q0_L_1fFQ_Q0_ : $@convention(thin) <τ_0_0, τ_0_1> (@out τ_0_1, @in τ_0_0) -> ()
|
|
// CHECK: [[FN_WITH_GENERIC_PARAMS:%.*]] = partial_apply [[FN]]<A, R>() : $@convention(thin) <τ_0_0, τ_0_1> (@out τ_0_1, @in τ_0_0) -> ()
|
|
// CHECK: return [[FN_WITH_GENERIC_PARAMS]] : $@callee_owned (@out R, @in A) -> ()
|
|
return f
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures17read_only_capture
|
|
func read_only_capture(var x: Int) -> Int {
|
|
// CHECK: bb0([[X:%[0-9]+]] : $Int):
|
|
// CHECK: [[XBOX:%[0-9]+]] = alloc_box $Int
|
|
|
|
func cap() -> Int {
|
|
return x
|
|
}
|
|
|
|
return cap()
|
|
// CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures17read_only_capture.*]] : $@convention(thin) (@owned Builtin.NativeObject, @inout Int) -> Int
|
|
// CHECK: [[RET:%[0-9]+]] = apply [[CAP]]([[XBOX]]#0, [[XBOX]]#1)
|
|
// CHECK: release [[XBOX]]#0
|
|
// CHECK: return [[RET]]
|
|
}
|
|
|
|
// CHECK: sil shared @[[CAP_NAME]]
|
|
// CHECK: bb0([[XBOX:%[0-9]+]] : $Builtin.NativeObject, [[XADDR:%[0-9]+]] : $*Int):
|
|
// CHECK: [[X:%[0-9]+]] = load [[XADDR]]
|
|
// CHECK: release [[XBOX]]
|
|
// CHECK: return [[X]]
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures16write_to_capture
|
|
func write_to_capture(var x: Int) -> Int {
|
|
// CHECK: bb0([[X:%[0-9]+]] : $Int):
|
|
// CHECK: [[XBOX:%[0-9]+]] = alloc_box $Int
|
|
// CHECK: [[X2BOX:%[0-9]+]] = alloc_box $Int
|
|
var x2 = x
|
|
|
|
func scribble() {
|
|
x2 = zero
|
|
}
|
|
|
|
scribble()
|
|
// CHECK: [[SCRIB:%[0-9]+]] = function_ref @[[SCRIB_NAME:_TFF8closures16write_to_capture.*]] : $@convention(thin) (@owned Builtin.NativeObject, @inout Int) -> ()
|
|
// CHECK: apply [[SCRIB]]([[X2BOX]]#0, [[X2BOX]]#1)
|
|
// CHECK: [[RET:%[0-9]+]] = load [[X2BOX]]#1
|
|
// CHECK: release [[X2BOX]]#0
|
|
// CHECK: release [[XBOX]]#0
|
|
// CHECK: return [[RET]]
|
|
return x2
|
|
}
|
|
|
|
// CHECK: sil shared @[[SCRIB_NAME]]
|
|
// CHECK: bb0([[XBOX:%[0-9]+]] : $Builtin.NativeObject, [[XADDR:%[0-9]+]] : $*Int):
|
|
// CHECK: copy_addr {{%[0-9]+}} to [[XADDR]]
|
|
// CHECK: release [[XBOX]]
|
|
// CHECK: return
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures21multiple_closure_refs
|
|
func multiple_closure_refs(var x: Int) -> (() -> Int, () -> Int) {
|
|
func cap() -> Int {
|
|
return x
|
|
}
|
|
|
|
return (cap, cap)
|
|
// CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures21multiple_closure_refs.*]] : $@convention(thin) (@owned Builtin.NativeObject, @inout Int) -> Int
|
|
// CHECK: [[CAP_CLOSURE_1:%[0-9]+]] = partial_apply [[CAP]]
|
|
// CHECK: [[CAP:%[0-9]+]] = function_ref @[[CAP_NAME:_TFF8closures21multiple_closure_refs.*]] : $@convention(thin) (@owned Builtin.NativeObject, @inout Int) -> Int
|
|
// CHECK: [[CAP_CLOSURE_2:%[0-9]+]] = partial_apply [[CAP]]
|
|
// CHECK: [[RET:%[0-9]+]] = tuple ([[CAP_CLOSURE_1]] : {{.*}}, [[CAP_CLOSURE_2]] : {{.*}})
|
|
// CHECK: return [[RET]]
|
|
}
|
|
|
|
/* TODO: Full support for references between local functions
|
|
// C/HECK-LABEL: sil hidden @_TF8closures18capture_local_func
|
|
func capture_local_func(var x: Int) -> () -> () -> Int {
|
|
// C/HECK: [[XBOX:%[0-9]+]] = alloc_box $Int
|
|
|
|
func aleph() -> Int { return x }
|
|
// C/HECK: [[ALEPH_REF:%[0-9]+]] = function_ref @[[ALEPH_NAME:_TFF8closures18capture_local_func.*]] : $@convention(thin) (@owned Builtin.NativeObject, @inout Int) -> Int
|
|
// C/HECK: [[ALEPH_CLOSURE:%[0-9]+]] = partial_apply [[ALEPH_REF]]([[XBOX]]#0, [[XBOX]]#1)
|
|
|
|
func beth() -> () -> Int { return aleph }
|
|
// C/HECK: [[BETH_REF:%[0-9]+]] = function_ref @[[BETH_NAME:_TFF8closures18capture_local_func.*]] : $@convention(thin) (@owned @callee_owned () -> Int) -> @owned @callee_owned () -> Int
|
|
// C/HECK: [[BETH_CLOSURE:%[0-9]+]] = partial_apply [[BETH_REF]]([[ALEPH_CLOSURE]])
|
|
|
|
return beth
|
|
// C/HECK: release [[ALEPH_CLOSURE]]
|
|
// C/HECK: release [[XBOX]]#0
|
|
// C/HECK: return [[BETH_CLOSURE]]
|
|
}
|
|
// C/HECK: sil shared @[[ALEPH_NAME]]
|
|
// C/HECK: bb0([[XBOX:%[0-9]+]] : $Builtin.NativeObject, [[XADDR:%[0-9]+]] : $*Int):
|
|
|
|
// C/HECK: sil shared @[[BETH_NAME]]
|
|
// C/HECK: bb0([[ALEPH:%[0-9]+]] : $@callee_owned () -> Int):
|
|
// C/HECK: return [[ALEPH]]
|
|
*/
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures22anon_read_only_capture
|
|
func anon_read_only_capture(var x: Int) -> Int {
|
|
// CHECK: bb0([[X:%[0-9]+]] : $Int):
|
|
// CHECK: [[XBOX:%[0-9]+]] = alloc_box $Int
|
|
|
|
return ({ x })()
|
|
// -- func expression
|
|
// CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures22anon_read_only_capture.*]] : $@convention(thin) (@inout Int) -> Int
|
|
// -- apply expression
|
|
// CHECK: [[RET:%[0-9]+]] = apply [[ANON]]([[XBOX]]#1)
|
|
// -- cleanup
|
|
// CHECK: release [[XBOX]]#0
|
|
// CHECK: return [[RET]]
|
|
}
|
|
// CHECK: sil shared @[[CLOSURE_NAME]]
|
|
// CHECK: bb0([[XADDR:%[0-9]+]] : $*Int):
|
|
// CHECK: [[X:%[0-9]+]] = load [[XADDR]]
|
|
// CHECK: return [[X]]
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures21small_closure_capture
|
|
func small_closure_capture(var x: Int) -> Int {
|
|
// CHECK: bb0([[X:%[0-9]+]] : $Int):
|
|
// CHECK: [[XBOX:%[0-9]+]] = alloc_box $Int
|
|
|
|
return { x }()
|
|
// -- func expression
|
|
// CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures21small_closure_capture.*]] : $@convention(thin) (@inout Int) -> Int
|
|
// -- apply expression
|
|
// CHECK: [[RET:%[0-9]+]] = apply [[ANON]]([[XBOX]]#1)
|
|
// -- cleanup
|
|
// CHECK: release [[XBOX]]#0
|
|
// CHECK: return [[RET]]
|
|
}
|
|
// CHECK: sil shared @[[CLOSURE_NAME]]
|
|
// CHECK: bb0([[XADDR:%[0-9]+]] : $*Int):
|
|
// CHECK: [[X:%[0-9]+]] = load [[XADDR]]
|
|
// CHECK: return [[X]]
|
|
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures35small_closure_capture_with_argument
|
|
func small_closure_capture_with_argument(var x: Int) -> (y: Int) -> Int {
|
|
// CHECK: [[XBOX:%[0-9]+]] = alloc_box $Int
|
|
|
|
return { x + $0 }
|
|
// -- func expression
|
|
// CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures35small_closure_capture_with_argument.*]] : $@convention(thin) (Int, @owned Builtin.NativeObject, @inout Int) -> Int
|
|
// CHECK: retain [[XBOX]]#0
|
|
// CHECK: [[ANON_CLOSURE_APP:%[0-9]+]] = partial_apply [[ANON]]([[XBOX]]#0, [[XBOX]]#1)
|
|
// -- return
|
|
// CHECK: release [[XBOX]]#0
|
|
// CHECK: return [[ANON_CLOSURE_APP]]
|
|
}
|
|
// CHECK: sil shared @[[CLOSURE_NAME]] : $@convention(thin) (Int, @owned Builtin.NativeObject, @inout Int) -> Int
|
|
// CHECK: bb0([[DOLLAR0:%[0-9]+]] : $Int, [[XBOX:%[0-9]+]] : $Builtin.NativeObject, [[XADDR:%[0-9]+]] : $*Int):
|
|
// CHECK: [[PLUS:%[0-9]+]] = function_ref @_TZFSsoi1pFTSiSi_Si{{.*}}
|
|
// CHECK: [[LHS:%[0-9]+]] = load [[XADDR]]
|
|
// CHECK: [[RET:%[0-9]+]] = apply [[PLUS]]([[LHS]], [[DOLLAR0]])
|
|
// CHECK: release [[XBOX]]
|
|
// CHECK: return [[RET]]
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures24small_closure_no_capture
|
|
func small_closure_no_capture() -> (y: Int) -> Int {
|
|
// CHECK: [[ANON:%[0-9]+]] = function_ref @[[CLOSURE_NAME:_TFF8closures24small_closure_no_captureFT_FT1ySi_SiU_FSiSi]] : $@convention(thin) (Int) -> Int
|
|
// CHECK: [[ANON_THICK:%[0-9]+]] = thin_to_thick_function [[ANON]] : ${{.*}} to $@callee_owned (Int) -> Int
|
|
// CHECK: return [[ANON_THICK]]
|
|
return { $0 }
|
|
}
|
|
// CHECK: sil shared @[[CLOSURE_NAME]] : $@convention(thin) (Int) -> Int
|
|
// CHECK: bb0([[YARG:%[0-9]+]] : $Int):
|
|
|
|
// CHECK-LABEL: sil hidden @_TF8closures17uncaptured_locals{{.*}} :
|
|
func uncaptured_locals(var x: Int) -> (Int, Int) {
|
|
// -- locals without captures are stack-allocated
|
|
// CHECK: bb0([[XARG:%[0-9]+]] : $Int):
|
|
// CHECK: [[XADDR:%[0-9]+]] = alloc_box $Int
|
|
// CHECK: store [[XARG]] to [[XADDR]]
|
|
|
|
var y = zero
|
|
// CHECK: [[YADDR:%[0-9]+]] = alloc_box $Int
|
|
return (x, y)
|
|
|
|
}
|
|
|
|
class SomeClass {
|
|
var x : Int = zero
|
|
|
|
init() {
|
|
x = { self.x }() // Closing over self.
|
|
}
|
|
}
|
|
|
|
// Closures within destructors <rdar://problem/15883734>
|
|
class SomeGenericClass<T> {
|
|
deinit {
|
|
var i: Int = zero
|
|
// CHECK: [[C1REF:%[0-9]+]] = function_ref @_TFFC8closures16SomeGenericClassd{{.*}} : $@convention(thin) <τ_0_0> (@inout Int) -> Int
|
|
// CHECK: apply [[C1REF]]<T>([[IBOX:%[0-9]+]]#1) : $@convention(thin) <τ_0_0> (@inout Int) -> Int
|
|
var x = { i + zero } ()
|
|
|
|
// CHECK: [[C2REF:%[0-9]+]] = function_ref @_TFFC8closures16SomeGenericClassdU0_FT{{.*}} : $@convention(thin) <τ_0_0> () -> Int
|
|
// CHECK: apply [[C2REF]]<T>() : $@convention(thin) <τ_0_0> () -> Int
|
|
var y = { zero } ()
|
|
}
|
|
|
|
// CHECK-LABEL: sil shared @_TFFC8closures16SomeGenericClassdU{{.*}} : $@convention(thin) <T> (@inout Int) -> Int
|
|
|
|
// CHECK-LABEL: sil shared @_TFFC8closures16SomeGenericClassdU0_FT{{.*}} : $@convention(thin) <T> () -> Int
|
|
}
|
|
|
|
// This is basically testing that the constraint system ranking
|
|
// function conversions as worse than others, and therefore performs
|
|
// the conversion within closures when possible.
|
|
class SomeSpecificClass : SomeClass {}
|
|
func takesSomeClassGenerator(fn : () -> SomeClass) {}
|
|
func generateWithConstant(x : SomeSpecificClass) {
|
|
takesSomeClassGenerator({ x })
|
|
}
|
|
// CHECK-LABEL: sil shared @_TFF8closures20generateWithConstant
|
|
// CHECK: bb0([[T0:%.*]] : $SomeSpecificClass):
|
|
// CHECK-NEXT: [[T1:%.*]] = upcast [[T0]] : $SomeSpecificClass to $SomeClass
|
|
// CHECK-NEXT: return [[T1]]
|
|
|
|
|
|
// Check the annoying case of capturing 'self' in a derived class 'init'
|
|
// method. We allocate a mutable box to deal with 'self' potentially being
|
|
// rebound by super.init, but 'self' is formally immutable and so is captured
|
|
// by value. <rdar://problem/15599464>
|
|
class Base {}
|
|
|
|
class SelfCapturedInInit : Base {
|
|
var foo : () -> SelfCapturedInInit
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures18SelfCapturedInInitcfMS0_FT_S0_ : $@convention(method) (@owned SelfCapturedInInit) -> @owned SelfCapturedInInit {
|
|
// CHECK: [[VAL:%.*]] = load {{%.*}} : $*SelfCapturedInInit
|
|
// CHECK: [[VAL:%.*]] = load {{%.*}} : $*SelfCapturedInInit
|
|
// CHECK: [[VAL:%.*]] = load {{%.*}} : $*SelfCapturedInInit
|
|
// CHECK: strong_retain [[VAL]] : $SelfCapturedInInit
|
|
// CHECK: partial_apply {{%.*}}([[VAL]]) : $@convention(thin) (@owned SelfCapturedInInit) -> @owned SelfCapturedInInit
|
|
override init() {
|
|
super.init()
|
|
foo = { self }
|
|
}
|
|
}
|
|
|
|
func takeClosure(fn: () -> Int) -> Int { return fn() }
|
|
|
|
class TestCaptureList {
|
|
var x = zero
|
|
|
|
func testUnowned() {
|
|
let aLet = self
|
|
takeClosure { aLet.x }
|
|
takeClosure { [unowned aLet] in aLet.x }
|
|
takeClosure { [weak aLet] in aLet!.x }
|
|
|
|
var aVar = self
|
|
takeClosure { aVar.x }
|
|
takeClosure { [unowned aVar] in aVar.x }
|
|
takeClosure { [weak aVar] in aVar!.x }
|
|
|
|
takeClosure { self.x }
|
|
takeClosure { [unowned self] in self.x }
|
|
takeClosure { [weak self] in self!.x }
|
|
|
|
takeClosure { [unowned newVal = TestCaptureList()] in newVal.x }
|
|
takeClosure { [weak newVal = TestCaptureList()] in newVal!.x }
|
|
}
|
|
}
|
|
|
|
class ClassWithIntProperty { final var x = 42 }
|
|
|
|
func closeOverLetLValue() {
|
|
let a : ClassWithIntProperty
|
|
a = ClassWithIntProperty()
|
|
|
|
takeClosure { a.x }
|
|
}
|
|
|
|
// The let property needs to be captured into a temporary stack slot so that it
|
|
// is loadable even though we capture the value.
|
|
// CHECK-LABEL: sil shared @_TFF8closures18closeOverLetLValueFT_T_U_FT_Si
|
|
// CHECK-NEXT: bb0(%0 : $ClassWithIntProperty):
|
|
// CHECK-NEXT: [[TMP:%.*]] = alloc_stack $ClassWithIntProperty // let a
|
|
// CHECK-NEXT: store %0 to [[TMP]]#1 : $*ClassWithIntProperty
|
|
// CHECK-NEXT: {{.*}} = load [[TMP]]#1 : $*ClassWithIntProperty
|
|
// CHECK-NEXT: {{.*}} = ref_element_addr {{.*}} : $ClassWithIntProperty, #ClassWithIntProperty.x
|
|
// CHECK-NEXT: {{.*}} = load {{.*}} : $*Int
|
|
// CHECK-NEXT: destroy_addr [[TMP]]#1 : $*ClassWithIntProperty
|
|
// CHECK-NEXT: dealloc_stack %1#0 : $*@local_storage ClassWithIntProperty
|
|
// CHECK-NEXT: return
|
|
|
|
|
|
|
|
// Use an external function so inout deshadowing cannot see its body.
|
|
@asmname("takesNoEscapeClosure")
|
|
func takesNoEscapeClosure(@noescape fn : () -> Int)
|
|
|
|
struct StructWithMutatingMethod {
|
|
var x = 42
|
|
|
|
mutating func mutatingMethod() {
|
|
// This should not capture the refcount of the self shadow.
|
|
takesNoEscapeClosure { x = 42; return x }
|
|
}
|
|
}
|
|
|
|
// Check that the address of self is passed in, but not the refcount pointer.
|
|
|
|
// CHECK-LABEL: sil hidden @_TFV8closures24StructWithMutatingMethod14mutatingMethodfRS0_FT_T_
|
|
// CHECK-NEXT: bb0(%0 : $*StructWithMutatingMethod):
|
|
// CHECK-NEXT: %1 = alloc_box $StructWithMutatingMethod // var self // users: %2, %5, %7, %8
|
|
// CHECK-NEXT: copy_addr %0 to [initialization] %1#1 : $*StructWithMutatingMethod // id: %2
|
|
// CHECK: [[CLOSURE:%[0-9]+]] = function_ref @_TFFV8closures24StructWithMutatingMethod14mutatingMethodFRS0_FT_T_U_FT_Si : $@convention(thin) (@inout StructWithMutatingMethod) -> Int
|
|
// CHECK: partial_apply [[CLOSURE]](%1#1) : $@convention(thin) (@inout StructWithMutatingMethod) -> Int
|
|
|
|
// Check that the closure body only takes the pointer.
|
|
// CHECK-LABEL: sil shared @_TFFV8closures24StructWithMutatingMethod14mutatingMethodFRS0_FT_T_U_FT_Si : $@convention(thin) (@inout StructWithMutatingMethod) -> Int {
|
|
// CHECK-NEXT: bb0(%0 : $*StructWithMutatingMethod):
|
|
|
|
class SuperBase {
|
|
func boom() {}
|
|
}
|
|
class SuperSub : SuperBase {
|
|
override func boom() {}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1afS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1aFS0_FT_T_L_2a1fT_T_
|
|
// CHECK: = apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func a() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1aFS0_FT_T_L_2a1fT_T_
|
|
// CHECK: [[CLASS_METHOD:%.*]] = class_method %0 : $SuperSub, #SuperSub.boom!1
|
|
// CHECK: = apply [[CLASS_METHOD]](%0)
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[SUPER_METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[SUPER_METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
func a1() {
|
|
self.boom()
|
|
super.boom()
|
|
}
|
|
a1()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1bfS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1bFS0_FT_T_L_2b1fT_T_
|
|
// CHECK: = apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func b() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1bFS0_FT_T_L_2b1fT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFFC8closures8SuperSub1bFS0_FT_T_L_2b1FT_T_L_2b2fT_T_
|
|
// CHECK: = apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func b1() {
|
|
// CHECK-LABEL: sil shared @_TFFFC8closures8SuperSub1bFS0_FT_T_L_2b1FT_T_L_2b2fT_T_
|
|
// CHECK: [[CLASS_METHOD:%.*]] = class_method %0 : $SuperSub, #SuperSub.boom!1
|
|
// CHECK: = apply [[CLASS_METHOD]](%0)
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
func b2() {
|
|
self.boom()
|
|
super.boom()
|
|
}
|
|
b2()
|
|
}
|
|
b1()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1cfS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1cFS0_FT_T_U_FT_T_
|
|
// CHECK: = partial_apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func c() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1cFS0_FT_T_U_FT_T_
|
|
// CHECK: [[CLASS_METHOD:%.*]] = class_method %0 : $SuperSub, #SuperSub.boom!1
|
|
// CHECK: = apply [[CLASS_METHOD]](%0)
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
let c1 = { () -> Void in
|
|
self.boom()
|
|
super.boom()
|
|
}
|
|
c1()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1dfS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1dFS0_FT_T_U_FT_T_
|
|
// CHECK: = partial_apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func d() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1dFS0_FT_T_U_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFFC8closures8SuperSub1dFS0_FT_T_U_FT_T_L_2d2fT_T_
|
|
// CHECK: = apply [[INNER]](%0)
|
|
// CHECK: return
|
|
let d1 = { () -> Void in
|
|
// CHECK-LABEL: sil shared @_TFFFC8closures8SuperSub1dFS0_FT_T_U_FT_T_L_2d2fT_T_
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
func d2() {
|
|
super.boom()
|
|
}
|
|
d2()
|
|
}
|
|
d1()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1efS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1eFS0_FT_T_L_2e1fT_T_
|
|
// CHECK: = apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func e() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1eFS0_FT_T_L_2e1fT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFFC8closures8SuperSub1eFS0_FT_T_L_2e1FT_T_U_FT_T_
|
|
// CHECK: = partial_apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func e1() {
|
|
// CHECK-LABEL: sil shared @_TFFFC8closures8SuperSub1eFS0_FT_T_L_2e1FT_T_U_FT_T_
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
let e2 = {
|
|
super.boom()
|
|
}
|
|
e2()
|
|
}
|
|
e1()
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1ffS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1fFS0_FT_T_U_FT_T_
|
|
// CHECK: = partial_apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func f() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1fFS0_FT_T_U_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFFC8closures8SuperSub1fFS0_FT_T_U_FT_T_u_KT_T_
|
|
// CHECK: = partial_apply [[INNER]](%0)
|
|
// CHECK: return
|
|
let f1 = {
|
|
// CHECK-LABEL: sil shared [transparent] @_TFFFC8closures8SuperSub1fFS0_FT_T_U_FT_T_u_KT_T_
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
nil ?? super.boom()
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures8SuperSub1gfS0_FT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFC8closures8SuperSub1gFS0_FT_T_L_2g1fT_T_
|
|
// CHECK: = apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func g() {
|
|
// CHECK-LABEL: sil shared @_TFFC8closures8SuperSub1gFS0_FT_T_L_2g1fT_T_
|
|
// CHECK: [[INNER:%.*]] = function_ref @_TFFFC8closures8SuperSub1gFS0_FT_T_L_2g1FT_T_u_KT_T_
|
|
// CHECK: = partial_apply [[INNER]](%0)
|
|
// CHECK: return
|
|
func g1() {
|
|
// CHECK-LABEL: sil shared [transparent] @_TFFFC8closures8SuperSub1gFS0_FT_T_L_2g1FT_T_u_KT_T_
|
|
// CHECK: [[SUPER:%.*]] = upcast %0 : $SuperSub to $SuperBase
|
|
// CHECK: [[METHOD:%.*]] = function_ref @_TFC8closures9SuperBase4boomfS0_FT_T_
|
|
// CHECK: = apply [[METHOD]]([[SUPER]])
|
|
// CHECK: return
|
|
nil ?? super.boom()
|
|
}
|
|
g1()
|
|
}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden @_TFC8closures24UnownedSelfNestedCapture13nestedCapturefS0_FT_T_ : $@convention(method) (@guaranteed UnownedSelfNestedCapture) -> ()
|
|
// CHECK: [[OUTER_SELF_CAPTURE:%.*]] = alloc_box $@sil_unowned UnownedSelfNestedCapture
|
|
// CHECK: [[UNOWNED_SELF:%.*]] = ref_to_unowned [[SELF_PARAM:%.*]] :
|
|
// -- TODO: A lot of fussy r/r traffic and owned/unowned conversions here.
|
|
// -- strong +1, unowned +1
|
|
// CHECK: unowned_retain [[UNOWNED_SELF]]
|
|
// CHECK: store [[UNOWNED_SELF]] to [[OUTER_SELF_CAPTURE]]
|
|
// CHECK: [[UNOWNED_SELF:%.*]] = load [[OUTER_SELF_CAPTURE]]
|
|
// -- strong +2, unowned +1
|
|
// CHECK: strong_retain_unowned [[UNOWNED_SELF]]
|
|
// CHECK: [[SELF:%.*]] = unowned_to_ref [[UNOWNED_SELF]]
|
|
// CHECK: [[UNOWNED_SELF2:%.*]] = ref_to_unowned [[SELF]]
|
|
// -- strong +2, unowned +2
|
|
// CHECK: unowned_retain [[UNOWNED_SELF2]]
|
|
// -- strong +1, unowned +2
|
|
// CHECK: strong_release [[SELF]]
|
|
// -- closure takes unowned ownership
|
|
// CHECK: [[OUTER_CLOSURE:%.*]] = partial_apply {{%.*}}([[UNOWNED_SELF2]])
|
|
// -- call consumes closure
|
|
// -- strong +1, unowned +1
|
|
// CHECK: [[INNER_CLOSURE:%.*]] = apply [[OUTER_CLOSURE]]
|
|
// CHECK: [[CONSUMED_RESULT:%.*]] = apply [[INNER_CLOSURE]]()
|
|
// CHECK: strong_release [[CONSUMED_RESULT]]
|
|
// -- releases unowned self in box
|
|
// -- strong +1, unowned +0
|
|
// CHECK: strong_release [[OUTER_SELF_CAPTURE]]
|
|
// -- strong +0, unowned +0
|
|
// CHECK: return
|
|
|
|
// -- outer closure
|
|
// -- strong +0, unowned +1
|
|
// CHECK-LABEL: sil shared @_TFFC8closures24UnownedSelfNestedCapture13nestedCaptureFS0_FT_T_U_FT_FT_S0_
|
|
// -- strong +0, unowned +2
|
|
// CHECK: unowned_retain [[CAPTURED_SELF:%.*]] :
|
|
// -- closure takes ownership of unowned ref
|
|
// CHECK: [[INNER_CLOSURE:%.*]] = partial_apply {{%.*}}([[CAPTURED_SELF]])
|
|
// -- strong +0, unowned +1 (claimed by closure)
|
|
// CHECK: unowned_release [[CAPTURED_SELF]]
|
|
// CHECK: return [[INNER_CLOSURE]]
|
|
|
|
// -- inner closure
|
|
// -- strong +0, unowned +1
|
|
// CHECK-LABEL: sil shared @_TFFFC8closures24UnownedSelfNestedCapture13nestedCaptureFS0_FT_T_U_FT_FT_S0_U_FT_S0_
|
|
// -- strong +1, unowned +1
|
|
// CHECK: strong_retain_unowned [[CAPTURED_SELF:%.*]] :
|
|
// CHECK: [[SELF:%.*]] = unowned_to_ref [[CAPTURED_SELF]]
|
|
// -- strong +1, unowned +0 (claimed by return)
|
|
// CHECK: unowned_release [[CAPTURED_SELF]]
|
|
// CHECK: return [[SELF]]
|
|
class UnownedSelfNestedCapture {
|
|
func nestedCapture() {
|
|
{[unowned self] in { self } }()()
|
|
}
|
|
}
|