blob: cb8548416ee92f0abc26f3d6a5bf598702d2fad7 [file] [log] [blame]
// RUN: %target-sil-opt -wmo -enable-sil-verify-all -emit-sorted-sil %s -enable-existential-specializer -existential-specializer -inline -sil-combine -generic-specializer -devirtualizer 2>&1 | %FileCheck %s
// This file tests existential specializer transformation followed by concrete type propagation and generic specialization leading to a devirtualization of a witness method call.
sil_stage canonical
import Builtin
import Swift
import SwiftShims
protocol RP {
func getThres() -> Int32
}
class RC : RP {
@inline(never) func getThres() -> Int32
}
sil hidden [noinline] @$s6test0_2RCC8getThress5Int32VyF : $@convention(method) (@guaranteed RC) -> Int32 {
bb0(%0 : $RC):
%1 = integer_literal $Builtin.Int32, 10
%2 = struct $Int32 (%1 : $Builtin.Int32)
return %2 : $Int32
}
sil private [transparent] [thunk] @$s6test0_2RCCAA2RPA2aDP8getThress5Int32VyFTW : $@convention(witness_method: RP) (@in_guaranteed RC) -> Int32 {
bb0(%0 : $*RC):
%1 = load %0 : $*RC
%2 = class_method %1 : $RC, #RC.getThres : (RC) -> () -> Int32, $@convention(method) (@guaranteed RC) -> Int32
%3 = apply %2(%1) : $@convention(method) (@guaranteed RC) -> Int32
return %3 : $Int32
}
sil_vtable RC {
#RC.getThres: (RC) -> () -> Int32 : @$s6test0_2RCC8getThress5Int32VyF
}
sil_witness_table hidden RC: RP module simple {
method #RP.getThres: <Self where Self : RP> (Self) -> () -> Int32 : @$s6test0_2RCCAA2RPA2aDP8getThress5Int32VyFTW
}
// Note: The function_ref of "function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool" in the transformed code was originally a "function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool" taking an existential parameter and has been generic specialized after existential specialization.
// CHECK-LABEL: sil hidden [noinline] @$s6test0_12find_wrapperSbyF : $@convention(thin) () -> Bool {
// CHECK: bb0:
// CHECK: alloc_ref
// CHECK: integer_literal
// CHECK: struct
// CHECK: alloc_stack
// CHECK: store
// CHECK: strong_retain
// CHECK: function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool
// CHECK: load
// CHECK: apply
// CHECK: destroy_addr
// CHECK: dealloc_stack
// CHECK: strong_release
// CHECK: return
// CHECK-LABEL: } // end sil function '$s6test0_12find_wrapperSbyF'
sil hidden [noinline] @$s6test0_12find_wrapperSbyF : $@convention(thin) () -> Bool {
bb0:
%0 = alloc_ref $RC
%3 = integer_literal $Builtin.Int32, 0
%4 = struct $Int32 (%3 : $Builtin.Int32)
%5 = alloc_stack $RP
%6 = init_existential_addr %5 : $*RP, $RC
store %0 to %6 : $*RC
%8 = function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
strong_retain %0 : $RC
%10 = apply %8(%4, %5) : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
destroy_addr %5 : $*RP
dealloc_stack %5 : $*RP
strong_release %0 : $RC
return %10 : $Bool
}
// Note 1: The function_ref of "function_ref @$s6test0_2RCC8getThress5Int32VyF : $@convention(method) (@guaranteed RC) -> Int32" in the transformed code was originally a witness method "witness_method $@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP, #RP.getThres : <Self where Self : RP> (Self) -> () -> Int32, %4 : $*@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP : $@convention(witness_method: RP) <τ_0_0 where τ_0_0 : RP> (@in_guaranteed τ_0_0) -> Int32" and has been devirtualized after existential specialization.
// Note 2: The function_ref of "function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool" in the transformed code was originally a "function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool" taking an existential parameter and has been generic specialized after existential specialization.
// CHECK-LABEL: sil shared [noinline] @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool {
// CHECK: bb0
// CHECK: alloc_stack
// CHECK: store
// CHECK: alloc_stack
// CHECK: init_existential_addr
// CHECK: copy_addr
// CHECK: open_existential_addr
// CHECK: unchecked_addr_cast
// CHECK: load
// CHECK: function_ref @$s6test0_2RCC8getThress5Int32VyF : $@convention(method) (@guaranteed RC) -> Int32
// CHECK: apply
// CHECK: struct_extract
// CHECK: struct_extract
// CHECK: builtin
// CHECK: cond_br
// CHECK: bb1:
// CHECK: integer_literal
// CHECK: struct
// CHECK: br
// CHECK: bb2:
// CHECK: integer_literal
// CHECK: integer_literal
// CHECK: builtin
// CHECK: tuple_extract
// CHECK: struct
// CHECK: function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5 : $@convention(thin) (Int32, @guaranteed RC) -> Bool
// CHECK: load
// CHECK: apply
// CHECK: br
// CHECK: bb3
// CHECK: dealloc_stack
// CHECK: dealloc_stack
// CHECK: return
// CHECK-LABEL: } // end sil function '$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptFTf4ne_n4main2RCC_Tg5'
sil hidden [noinline] @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool {
bb0(%0 : $Int32, %1 : $*RP):
%4 = open_existential_addr immutable_access %1 : $*RP to $*@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP
%5 = witness_method $@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP, #RP.getThres : <Self where Self : RP> (Self) -> () -> Int32, %4 : $*@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP : $@convention(witness_method: RP) <τ_0_0 where τ_0_0 : RP> (@in_guaranteed τ_0_0) -> Int32
%6 = apply %5<@opened("7AE0C0CA-28D3-11E9-B1D4-F21898973DF0") RP>(%4) : $@convention(witness_method: RP) _0_0 where τ_0_0 : RP> (@in_guaranteed τ_0_0) -> Int32
%7 = struct_extract %0 : $Int32, #Int32._value
%8 = struct_extract %6 : $Int32, #Int32._value
%9 = builtin "cmp_slt_Int32"(%7 : $Builtin.Int32, %8 : $Builtin.Int32) : $Builtin.Int1
cond_br %9, bb2, bb1
bb1:
%11 = integer_literal $Builtin.Int1, -1
%12 = struct $Bool (%11 : $Builtin.Int1)
br bb3(%12 : $Bool)
bb2:
%14 = integer_literal $Builtin.Int32, 1
%15 = integer_literal $Builtin.Int1, -1
%16 = builtin "sadd_with_overflow_Int32"(%7 : $Builtin.Int32, %14 : $Builtin.Int32, %15 : $Builtin.Int1) : $(Builtin.Int32, Builtin.Int1)
%17 = tuple_extract %16 : $(Builtin.Int32, Builtin.Int1), 0
%18 = struct $Int32 (%17 : $Builtin.Int32)
%19 = function_ref @$s6test0_4find5count4Obj1Sbs5Int32V_AA2RP_ptF : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
%20 = apply %19(%18, %1) : $@convention(thin) (Int32, @in_guaranteed RP) -> Bool
br bb3(%20 : $Bool)
bb3(%22 : $Bool):
return %22 : $Bool
}
// <rdar://problem/49336444> SILCombine infinite loop.
//
// Test a apply argument from an init_existential with a sole
// conforming type. We currently bail on rewriting the apply because
// it returns the same substituted type. Avoid infinitely iterating in
// SILCombine due to repeatedly creating an destroying the same cast.
public protocol BaseProtocol {
func testProtocolMethod() -> Self
}
extension BaseProtocol {
func testProtocolMethod() -> Self {
return self
}
}
protocol SubProtocol : BaseProtocol {}
final class ClassImpl : SubProtocol {}
extension ClassImpl {
final func testProtocolMethod() -> ClassImpl
}
sil @$s6testProtocolMethod : $@convention(method) _0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
// Verify that the optimization was not performance and that we don't hang as a result.
// CHECK-LABEL: sil hidden @$s6test1_ConcreteInitExistential : $@convention(method) (@in SubProtocol) -> () {
// CHECK: [[E:%.*]] = init_existential_addr %{{.*}} : $*SubProtocol, $@opened("{{.*}}") SubProtocol
// CHECK: apply %{{.*}}<@opened("{{.*}}") SubProtocol>([[E]], %{{.*}}) : $@convention(method) <τ_0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
// CHECK-LABEL: } // end sil function '$s6test1_ConcreteInitExistential'
sil hidden @$s6test1_ConcreteInitExistential : $@convention(method) (@in SubProtocol) -> () {
bb0(%0 : $*SubProtocol):
%10 = open_existential_addr immutable_access %0 : $*SubProtocol to $*@opened("CA90348E-5376-11E9-8C51-ACDE48001122") SubProtocol
%11 = alloc_stack $SubProtocol
%15 = function_ref @$s6testProtocolMethod : $@convention(method) _0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
%16 = init_existential_addr %11 : $*SubProtocol, $@opened("CA90348E-5376-11E9-8C51-ACDE48001122") SubProtocol
%17 = apply %15<@opened("CA90348E-5376-11E9-8C51-ACDE48001122") SubProtocol>(%16, %10) : $@convention(method) _0_0 where τ_0_0 : BaseProtocol> (@in_guaranteed τ_0_0) -> @out τ_0_0
dealloc_stack %11 : $*SubProtocol
%80 = tuple ()
return %80 : $()
}
//===----------------------------------------------------------------------===//
// Test an existential argument that is consumed and explicitly destroyed.
// <rdar://problem/50595630> Multiple leaks detected
//===----------------------------------------------------------------------===//
public protocol P {}
public struct S : P {
init()
var o: AnyObject // nontrivial to check ownership conventions
}
// Check the thunk.
// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @$s6test2_6storePyyAA1P_p_AaC_pztF : $@convention(thin) (@in P, @inout P) -> () {
// CHECK: bb0(%0 : $*P, %1 : $*P):
// CHECK: [[F:%.*]] = function_ref @$s6test2_6storePyyAA1P_p_AaC_pztFTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in τ_0_0, @inout P) -> ()
// CHECK: [[ADR:%.*]] = open_existential_addr mutable_access %0 : $*P to $*@opened([[TY:".*"]]) P
// CHECK: [[ALLOC:%.*]] = alloc_stack $@opened([[TY]]) P
// CHECK: copy_addr [[ADR]] to [initialization] [[ALLOC]] : $*@opened([[TY]]) P
// CHECK: apply [[F]]<@opened("{{.*}}") P>([[ALLOC]], %1) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in τ_0_0, @inout P) -> ()
// CHECK: destroy_addr %0 : $*P
// CHECK: dealloc_stack [[ALLOC]] : $*@opened([[TY]]) P
// CHECK: return
// CHECK-LABEL: } // end sil function '$s6test2_6storePyyAA1P_p_AaC_pztF'
// Check the existential to generic specialization.
// CHECK-LABEL: sil shared [noinline] @$s6test2_6storePyyAA1P_p_AaC_pztFTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in τ_0_0, @inout P) -> () {
// CHECK: bb0(%0 : $*τ_0_0, %1 : $*P):
// CHECK: [[ALLOC:%.*]] = alloc_stack $P
// CHECK: [[IEA:%.*]] = init_existential_addr [[ALLOC]] : $*P, $τ_0_0
// CHECK: copy_addr [take] %0 to [initialization] [[IEA]] : $*τ_0_0
// CHECK-NOT: destroy
// CHECK: copy_addr [[ALLOC]] to [initialization] %1 : $*P
// CHECK-NOT: destroy
// CHECK: destroy_addr [[ALLOC]] : $*P
// CHECK-NOT: destroy
// CHECK-LABEL: } // end sil function '$s6test2_6storePyyAA1P_p_AaC_pztFTf4en_n'
// Check the generic to concrete specialization.
// CHECK-LABEL: sil shared [noinline] @$s6test2_6storePyyAA1P_p_AaC_pztFTf4en_n4main1SV_Tg5 : $@convention(thin) (@owned S, @inout P) -> () {
// CHECK: bb0(%0 : $S, %1 : $*P):
// CHECK: [[ALLOCS:%.*]] = alloc_stack $S
// CHECK: [[ALLOCP:%.*]] = alloc_stack $P
// CHECK: [[IEA:%.*]] = init_existential_addr [[ALLOCP]] : $*P, $S
// CHECK: copy_addr [take] [[ALLOCS]] to [initialization] [[IEA]] : $*S
// CHECK-NOT: destroy
// CHECK: copy_addr [[ALLOCP]] to [initialization] %1 : $*P
// CHECK: destroy_addr [[ALLOCP]] : $*P
// CHECK: dealloc_stack [[ALLOCP]]
// CHECK: dealloc_stack [[ALLOCS]]
// CHECK-LABEL: } // end sil function '$s6test2_6storePyyAA1P_p_AaC_pztFTf4en_n4main1SV_Tg5'
sil hidden [noinline] @$s6test2_6storePyyAA1P_p_AaC_pztF : $@convention(thin) (@in P, @inout P) -> () {
bb0(%0 : $*P, %1 : $*P):
copy_addr %0 to [initialization] %1 : $*P
destroy_addr %0 : $*P
%5 = tuple ()
return %5 : $()
}
// CHECK-LABEL: sil @$s6test2_6storeS1s1qyAA1SV_AA1P_pztF : $@convention(thin) (S, @inout P) -> () {
// CHECK: bb0(%0 : $S, %1 : $*P):
// CHECK: [[ALLOCS1:%.*]] = alloc_stack $S
// CHECK: store %0 to [[ALLOCS1]] : $*S
// CHECK: [[ALLOCS2:%.*]] = alloc_stack $S
// CHECK: copy_addr [[ALLOCS1]] to [initialization] [[ALLOCS2]] : $*S
// CHECK: [[F:%.*]] = function_ref @$s6test2_6storePyyAA1P_p_AaC_pztFTf4en_n4main1SV_Tg5 : $@convention(thin) (@owned S, @inout P) -> ()
// CHECK: [[S:%.*]] = load [[ALLOCS2]] : $*S
// CHECK: apply [[F]]([[S]], %1) : $@convention(thin) (@owned S, @inout P) -> ()
// CHECK-NEXT: dealloc_stack [[ALLOCS2]] : $*S
// CHECK-NEXT: destroy_addr [[ALLOCS1]] : $*S
// CHECK-NEXT: dealloc_stack [[ALLOCS1]] : $*S
// CHECK-NEXT: tuple
// CHECK-NEXT: return
// CHECK-LABEL: } // end sil function '$s6test2_6storeS1s1qyAA1SV_AA1P_pztF'
sil @$s6test2_6storeS1s1qyAA1SV_AA1P_pztF : $@convention(thin) (S, @inout P) -> () {
bb0(%0 : $S, %1 : $*P):
%4 = alloc_stack $P
%5 = init_existential_addr %4 : $*P, $S
store %0 to %5 : $*S
%7 = function_ref @$s6test2_6storePyyAA1P_p_AaC_pztF : $@convention(thin) (@in P, @inout P) -> ()
%8 = apply %7(%4, %1) : $@convention(thin) (@in P, @inout P) -> ()
dealloc_stack %4 : $*P
%10 = tuple ()
return %10 : $()
}
sil_witness_table S: P module test {}
//===----------------------------------------------------------------------===//
// Test an existential in_guaranteed argument.
//===----------------------------------------------------------------------===//
// CHECK-LABEL: sil hidden [signature_optimized_thunk] [always_inline] @$s6test3_6storePyyAA1P_pz_AaC_pztF : $@convention(thin) (@in_guaranteed P, @inout P) -> () {
// CHECK: bb0(%0 : $*P, %1 : $*P):
// CHECK: [[F:%.*]] = function_ref @$s6test3_6storePyyAA1P_pz_AaC_pztFTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0, @inout P) -> ()
// CHECK: [[ADR:%.*]] = open_existential_addr immutable_access %0 : $*P to $*@opened("{{.*}}") P
// CHECK: apply [[F]]<@opened("{{.*}}") P>([[ADR]], %1) : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0, @inout P) -> ()
// CHECK-LABEL: } // end sil function '$s6test3_6storePyyAA1P_pz_AaC_pztF'
// The guaranteed concrete argument cannot be consumed. The
// existential created in the prolog must be destroyed before
// returing.
//
// CHECK-LABEL: sil shared @$s6test3_6storePyyAA1P_pz_AaC_pztFTf4en_n : $@convention(thin) <τ_0_0 where τ_0_0 : P> (@in_guaranteed τ_0_0, @inout P) -> () {
// CHECK: bb0(%0 : $*τ_0_0, %1 : $*P):
// CHECK: [[ALLOCP1:%.*]] = alloc_stack $P
// CHECK: [[ADR:%.*]] = init_existential_addr [[ALLOCP1]] : $*P, $τ_0_0
// CHECK: copy_addr %0 to [initialization] [[ADR]] : $*τ_0_0
// CHECK: [[ALLOCP2:%.*]] = alloc_stack $P
// CHECK: copy_addr [[ALLOCP1]] to [initialization] [[ALLOCP2]] : $*P
// CHECK: copy_addr [take] [[ALLOCP2]] to %1 : $*P
// CHECK: destroy_addr [[ALLOCP1]] : $*P
// CHECK: dealloc_stack [[ALLOCP1]] : $*P
// CHECK-LABEL: } // end sil function '$s6test3_6storePyyAA1P_pz_AaC_pztFTf4en_n'
sil hidden @$s6test3_6storePyyAA1P_pz_AaC_pztF : $@convention(thin) (@in_guaranteed P, @inout P) -> () {
bb0(%0 : $*P, %1 : $*P):
%5 = alloc_stack $P
copy_addr %0 to [initialization] %5 : $*P
copy_addr [take] %5 to %1 : $*P
dealloc_stack %5 : $*P
%12 = tuple ()
return %12 : $()
}
// CHECK-LABEL: sil shared @$s6test3_6storePyyAA1P_pz_AaC_pztFTf4en_n4main1SV_Tg5 : $@convention(thin) (@guaranteed S, @inout P) -> () {
// CHECK: bb0(%0 : $S, %1 : $*P):
// CHECK: [[ALLOC1:%.*]] = alloc_stack $S
// CHECK: store %0 to [[ALLOC1]] : $*S
// CHECK: [[ALLOC2:%.*]] = alloc_stack $P
// CHECK: [[ADR:%.*]] = init_existential_addr [[ALLOC2]] : $*P, $S
// CHECK: copy_addr [[ALLOC1]] to [initialization] [[ADR]] : $*S
// CHECK: [[ALLOC3:%.*]] = alloc_stack $P
// CHECK: copy_addr [[ALLOC2]] to [initialization] [[ALLOC3]] : $*P
// CHECK: copy_addr [take] [[ALLOC3]] to %1 : $*P
// CHECK: dealloc_stack [[ALLOC3]] : $*P
// CHECK: destroy_addr [[ALLOC2]] : $*P
// CHECK: dealloc_stack %4 : $*P
// CHECK: dealloc_stack %2 : $*S
// CHECK-LABEL: } // end sil function '$s6test3_6storePyyAA1P_pz_AaC_pztFTf4en_n4main1SV_Tg5'
sil hidden @$s6test3_6storeS1s1qyAA1SV_AA1P_pztF : $@convention(thin) (S, @inout P) -> () {
bb0(%0 : $S, %1 : $*P):
%4 = alloc_stack $P, var, name "sp"
%5 = init_existential_addr %4 : $*P, $S
store %0 to %5 : $*S
%9 = function_ref @$s6test3_6storePyyAA1P_pz_AaC_pztF : $@convention(thin) (@in_guaranteed P, @inout P) -> ()
%10 = apply %9(%4, %1) : $@convention(thin) (@in_guaranteed P, @inout P) -> ()
destroy_addr %4 : $*P
dealloc_stack %4 : $*P
%15 = tuple ()
return %15 : $()
}
//===----------------------------------------------------------------------===//
// Test an existential inout argument.
//===----------------------------------------------------------------------===//
// The ExistentialSpecializer cannot specialize on an @inout argument
// CHECK-LABEL: sil hidden @$s6test4_6storePyyAA1P_pz_AaC_pztF : $@convention(thin) (@inout P, @inout P) -> () {
// CHECK: bb0(%0 : $*P, %1 : $*P):
// CHECK: [[ALLOC:%.*]] = alloc_stack $P
// CHECK: copy_addr %0 to [initialization] [[ALLOC]] : $*P
// CHECK: copy_addr [take] [[ALLOC]] to %1 : $*P
// CHECK: dealloc_stack [[ALLOC]] : $*P
// CHECK-LABEL: } // end sil function '$s6test4_6storePyyAA1P_pz_AaC_pztF'
sil hidden @$s6test4_6storePyyAA1P_pz_AaC_pztF : $@convention(thin) (@inout P, @inout P) -> () {
bb0(%0 : $*P, %1 : $*P):
%5 = alloc_stack $P
copy_addr %0 to [initialization] %5 : $*P
copy_addr [take] %5 to %1 : $*P
dealloc_stack %5 : $*P
%12 = tuple ()
return %12 : $()
}
// ...and there's currently no way to propagate the concrete value to the inout argument!
// CHECK-LABEL: sil hidden @$s6test4_6storeS1s1qyAA1SV_AA1P_pztF : $@convention(thin) (S, @inout P) -> () {
// CHECK: bb0(%0 : $S, %1 : $*P):
// CHECK: [[ALLOC:%.*]] = alloc_stack $P, var, name "sp"
// CHECK: [[ADR1:%.*]] = init_existential_addr [[ALLOC]] : $*P, $S
// CHECK: store %0 to [[ADR1]] : $*S
// CHECK: [[ALLOC2:%.*]] = alloc_stack $P
// CHECK: copy_addr %2 to [initialization] %5 : $*P
// CHECK: copy_addr [take] %5 to %1 : $*P
// CHECK: destroy_addr [[ALLOC]] : $*P
// CHECK: dealloc_stack [[ALLOC]] : $*P
// CHECK-LABEL: } // end sil function '$s6test4_6storeS1s1qyAA1SV_AA1P_pztF'
sil hidden @$s6test4_6storeS1s1qyAA1SV_AA1P_pztF : $@convention(thin) (S, @inout P) -> () {
bb0(%0 : $S, %1 : $*P):
%4 = alloc_stack $P, var, name "sp"
%5 = init_existential_addr %4 : $*P, $S
store %0 to %5 : $*S
%9 = function_ref @$s6test4_6storePyyAA1P_pz_AaC_pztF : $@convention(thin) (@inout P, @inout P) -> ()
%10 = apply %9(%4, %1) : $@convention(thin) (@inout P, @inout P) -> ()
destroy_addr %4 : $*P
dealloc_stack %4 : $*P
%15 = tuple ()
return %15 : $()
}
//===----------------------------------------------------------------------===//
// Test an existential with a generic constraint
// rdar://56923071; [SR-11714]: Compiler crash when generic class is passed as
// a parameter to a function accepting a generic class
//
// Allowing this results in the error:
// SIL verification failed: Operand is of an ArchetypeType that does not exist
// in the Caller's generic param list.: isArchetypeValidInFunction(A, F)
//
// This could be fixed by preserving the original function's
// archetypes when rewriting the generic signature. However, the
// ExistentialSpecializer should not specialize generic functions in
// the first place because, without partial specialization, there's no
// guarantee that the new generic form will be able to be
// specialized. Preferably, all other generic parameters will be
// specialized first before attempting to specialize the existential.
//===----------------------------------------------------------------------===//
public class ClassA<T> { }
public protocol ProtocolA {
func foo() -> Int
}
public class ClassB<T> : ClassA<T> {
public func foo() -> Int
override init()
}
extension ClassB : ProtocolA {}
// Pass an existential argument that happens to also have a generic constraint.
//
// CHECK-LABEL: sil @testExistentialGenericConstraint : $@convention(thin) <T> (@guaranteed ClassB<T>) -> Int {
// CHECK: [[E:%.*]] = init_existential_ref %0 : $ClassB<T> : $ClassB<T>, $ClassA<T> & ProtocolA
// CHECK: [[F:%.*]] = function_ref @testExistentialGenericConstraintHelper : $@convention(thin) <τ_0_0> (@guaranteed ClassA<τ_0_0> & ProtocolA) -> Int
// CHECK: apply [[F]]<T>([[E]]) : $@convention(thin) <τ_0_0> (@guaranteed ClassA<τ_0_0> & ProtocolA) -> Int
// CHECK-LABEL: } // end sil function 'testExistentialGenericConstraint'
sil @testExistentialGenericConstraint : $@convention(thin) <T> (@guaranteed ClassB<T>) -> Int {
bb0(%0 : $ClassB<T>):
strong_retain %0 : $ClassB<T>
%3 = init_existential_ref %0 : $ClassB<T> : $ClassB<T>, $ClassA<T> & ProtocolA
%4 = function_ref @testExistentialGenericConstraintHelper : $@convention(thin) _0_0> (@guaranteed ClassA_0_0> & ProtocolA) -> Int
%5 = apply %4<T>(%3) : $@convention(thin) _0_0> (@guaranteed ClassA_0_0> & ProtocolA) -> Int
strong_release %3 : $ClassA<T> & ProtocolA
return %5 : $Int
}
// This generic function should not be "specialized" by ExistentialSpecializer, which would add more generic parameters.
//
// CHECK-LABEL: sil hidden @testExistentialGenericConstraintHelper : $@convention(thin) <T> (@guaranteed ClassA<T> & ProtocolA) -> Int {
// CHECK: bb0(%0 : $ClassA<T> & ProtocolA):
// CHECK-LABEL: } // end sil function 'testExistentialGenericConstraintHelper'
sil hidden @testExistentialGenericConstraintHelper : $@convention(thin) <T> (@guaranteed ClassA<T> & ProtocolA) -> Int {
bb0(%0 : $ClassA<T> & ProtocolA):
%2 = open_existential_ref %0 : $ClassA<T> & ProtocolA to $@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA
%3 = alloc_stack $@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA
store %2 to %3 : $*@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA
%5 = witness_method $@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA, #ProtocolA.foo : <Self where Self : ProtocolA> (Self) -> () -> Int, %2 : $@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA : $@convention(witness_method: ProtocolA) <τ_0_0 where τ_0_0 : ProtocolA> (@in_guaranteed τ_0_0) -> Int
%6 = apply %5<@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA>(%3) : $@convention(witness_method: ProtocolA) _0_0 where τ_0_0 : ProtocolA> (@in_guaranteed τ_0_0) -> Int
dealloc_stack %3 : $*@opened("92C29DA8-5479-11EA-8668-ACDE48001122") ClassA<T> & ProtocolA
return %6 : $Int
}