mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
A constructor can constrain generic parameters more than the type itself, either because there is a 'where' clause on the constructor, or because the constructor is defined inside an extension with a 'where' clause. In this case, when the constructor calls a stored property initializer to implicitly initialize a stored property with an initial value, we must apply substitutions to get the correct result type for the call. If the original type of the stored property lowers differently based on the abstraction pattern, for example if it is a function type, then emitApply() would by default re-abstract the result to the most specific abstraction pattern possible. However, this was wrong because we store the result value into the stored property, and a stored property's type should always be lowered with the most generic abstraction pattern. In practice, this meant if you have a stored property of type (T) -> (), and an initializer with 'where T == String' for example, we would call the initializer, to produce a value with lowered type (@in_guaranteed String) -> (), then thunk it to a (@guaranteed String) -> (), and try to store the thunk into the stored property -- which has type (@in_guaranteed String) -> (). This would either miscompile or trigger an assertion. To get around this, we want to bypass the orig-to-subst conversion performed in emitApply(). My chosen solution is to have emitApply() emit the result into a ConvertingInitialization set up to perform a subst-to-orig conversion. Now that ConvertingInitialization is smart enough to peephole away matched pairs of orig-to-subst and subst-to-orig conversions, this always reduces to a no-op, and the emitApply() call produces and stores a value with the correct abstraction pattern. Fixes <rdar://problem/67419937>.
39 lines
2.3 KiB
Swift
39 lines
2.3 KiB
Swift
// RUN: %target-swift-emit-silgen %s | %FileCheck %s
|
|
|
|
// rdar://problem/67419937
|
|
|
|
class Class<T> {
|
|
var fn: ((T) -> ())? = nil
|
|
|
|
init(_: T) where T == Int {}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s34stored_property_init_reabstraction5ClassCyACySiGSicSiRszlufc : $@convention(method) (Int, @owned Class<Int>) -> @owned Class<Int> {
|
|
// CHECK: [[SELF:%.*]] = mark_uninitialized [rootself] %1 : $Class<Int>
|
|
// CHECK: [[BORROW:%.*]] = begin_borrow [[SELF]] : $Class<Int>
|
|
// CHECK: [[ADDR:%.*]] = ref_element_addr [[BORROW]] : $Class<Int>, #Class.fn
|
|
// CHECK: [[INIT:%.*]] = function_ref @$s34stored_property_init_reabstraction5ClassC2fnyxcSgvpfi : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>>
|
|
// CHECK: [[VALUE:%.*]] = apply [[INIT]]<Int>() : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>>
|
|
// CHECK: store [[VALUE]] to [init] [[ADDR]] : $*Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <Int>>
|
|
// CHECK: end_borrow [[BORROW]] : $Class<Int>
|
|
|
|
struct Struct<T> {
|
|
var fn: ((T) -> ())? = nil
|
|
|
|
init(_: T) where T == Int {}
|
|
}
|
|
|
|
// CHECK-LABEL: sil hidden [ossa] @$s34stored_property_init_reabstraction6StructVyACySiGSicSiRszlufC : $@convention(method) (Int, @thin Struct<Int>.Type) -> @owned Struct<Int> {
|
|
// CHECK: [[SELF_BOX:%.*]] = mark_uninitialized [rootself] {{%.*}} : ${ var Struct<Int> }
|
|
// CHECK: [[SELF:%.*]] = project_box [[SELF_BOX]] : ${ var Struct<Int> }, 0
|
|
// CHECK: [[ADDR:%.*]] = struct_element_addr [[SELF]] : $*Struct<Int>, #Struct.fn
|
|
// CHECK: [[INIT:%.*]] = function_ref @$s34stored_property_init_reabstraction6StructV2fnyxcSgvpfi : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>>
|
|
// CHECK: [[VALUE:%.*]] = apply [[INIT]]<Int>() : $@convention(thin) <τ_0_0> () -> @owned Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <τ_0_0>>
|
|
// CHECK: store [[VALUE]] to [init] [[ADDR]] : $*Optional<@callee_guaranteed @substituted <τ_0_0> (@in_guaranteed τ_0_0) -> () for <Int>>
|
|
|
|
struct ComplexExample<T, U> {
|
|
var (fn, value): (((T) -> ())?, U?) = (nil, nil)
|
|
var anotherValue: (((T) -> ())?, U?) = (nil, nil)
|
|
|
|
init(_: T) where T == String {}
|
|
} |