|
| 1 | +// RUN: %target-swift-frontend -O -enable-ossa-modules -Xllvm -sil-print-types -emit-sil -sil-verify-all -Xllvm -sil-disable-pass=function-signature-opts %s | %FileCheck %s |
| 2 | +// RUN: %target-swift-frontend -O -enable-ossa-modules -Xllvm -sil-print-types -emit-sil -Xllvm -sil-verify-force-analysis-around-pass=devirtualizer -Xllvm -sil-disable-pass=function-signature-opts %s | %FileCheck %s |
| 3 | + |
| 4 | +// REQUIRES: swift_in_compiler |
| 5 | + |
| 6 | +//===----------------------------------------------------------------------===// |
| 7 | +// testReturnSelf: Call to a protocol extension method with |
| 8 | +// an existential self that can be type-propagated. |
| 9 | +// sil-combine should bailout since it does not propagate |
| 10 | +// type substitutions on the return value. |
| 11 | +// |
| 12 | +// rdar://40555427 |
| 13 | +// https://github.com/apple/swift/issues/50312 |
| 14 | +// 'SILCombiner::propagateConcreteTypeOfInitExistential' fails to fully |
| 15 | +// propagate type substitutions. |
| 16 | +//===----------------------------------------------------------------------===// |
| 17 | +public protocol P: class {} |
| 18 | + |
| 19 | +extension P { |
| 20 | + public func returnSelf() -> Self { |
| 21 | + return self |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +final class C: P {} |
| 26 | +// CHECK-LABEL: sil @$s37sil_combine_concrete_existential_ossa14testReturnSelfAA1P_pyF : $@convention(thin) () -> @owned any P { |
| 27 | +// CHECK: [[EI:%.*]] = end_init_let_ref %0 |
| 28 | +// CHECK: [[E1:%.*]] = init_existential_ref [[EI]] : $C : $C, $any P |
| 29 | +// CHECK: [[O1:%.*]] = open_existential_ref [[E1]] : $any P to $@opened("{{.*}}", any P) Self |
| 30 | +// CHECK: [[F1:%.*]] = function_ref @$s37sil_combine_concrete_existential_ossa1PPAAE10returnSelfxyF : $@convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> @owned τ_0_0 |
| 31 | +// CHECK: [[C1:%.*]] = apply [[F1]]<@opened("{{.*}}", any P) Self>([[O1]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> @owned τ_0_0 |
| 32 | +// CHECK: [[E2:%.*]] = init_existential_ref [[C1]] : $@opened("{{.*}}", any P) Self : $@opened("{{.*}}", any P) Self, $any P |
| 33 | +// CHECK: [[O2:%.*]] = open_existential_ref [[E2]] : $any P to $@opened("{{.*}}", any P) Self |
| 34 | +// CHECK: apply [[F1]]<@opened("{{.*}}", any P) Self>([[O2]]) : $@convention(method) <τ_0_0 where τ_0_0 : P> (@guaranteed τ_0_0) -> @owned τ_0_0 |
| 35 | +// CHECK-LABEL: } // end sil function '$s37sil_combine_concrete_existential_ossa14testReturnSelfAA1P_pyF' |
| 36 | +public func testReturnSelf() -> P { |
| 37 | + let p: P = C() |
| 38 | + return p.returnSelf().returnSelf() |
| 39 | +} |
| 40 | + |
| 41 | +//===----------------------------------------------------------------------===// |
| 42 | +// testWitnessReturnOptionalSelf: Call to a witness method with an existential |
| 43 | +// self that can be type-propagated. sil-combine should bailout since it does |
| 44 | +// not propagate type substitutions on the return value, and it must walk the |
| 45 | +// Optional type to find Self in the return type. |
| 46 | +// |
| 47 | +// Although sil-combine will not replace the self operand, it will still |
| 48 | +// rewrite the witness_method. The devirtualizer then handles the first call. |
| 49 | +//===----------------------------------------------------------------------===// |
| 50 | +public protocol PP: class { |
| 51 | + func returnOptionalSelf() -> Self? |
| 52 | +} |
| 53 | + |
| 54 | +final class CC: PP { |
| 55 | + init() {} |
| 56 | + func returnOptionalSelf() -> Self? { |
| 57 | + return self |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +// CHECK-LABEL: sil @$s37sil_combine_concrete_existential_ossa29testWitnessReturnOptionalSelfAA2PP_pSgyF : $@convention(thin) () -> @owned Optional<any PP> { |
| 62 | +// CHECK: [[EI:%.*]] = end_init_let_ref %0 |
| 63 | +// CHECK: [[E1:%.*]] = init_existential_ref [[EI]] : $CC : $CC, $any PP |
| 64 | +// CHECK: [[O1:%.*]] = open_existential_ref [[E1]] : $any PP to $@opened("{{.*}}", any PP) Self |
| 65 | +// CHECK: [[E2:%.*]] = init_existential_ref %{{.*}} : $@opened("{{.*}}", any PP) Self : $@opened("{{.*}}", any PP) Self, $any PP |
| 66 | +// CHECK: [[O2:%.*]] = open_existential_ref [[E2]] : $any PP to $@opened("{{.*}}", any PP) Self |
| 67 | +// CHECK: [[U1:%.*]] = unchecked_ref_cast [[EI]] : $CC to $@opened("{{.*}}", any PP) Self |
| 68 | +// CHECK: [[E3:%.*]] = init_existential_ref [[U1]] : $@opened("{{.*}}", any PP) Self : $@opened("{{.*}}", any PP) Self, $any PP |
| 69 | +// CHECK: [[E:%.*]] = enum $Optional<any PP>, #Optional.some!enumelt, [[E3]] : $any PP |
| 70 | +// CHECK: return [[E]] : $Optional<any PP> |
| 71 | +// CHECK-LABEL: } // end sil function '$s37sil_combine_concrete_existential_ossa29testWitnessReturnOptionalSelfAA2PP_pSgyF' |
| 72 | +public func testWitnessReturnOptionalSelf() -> PP? { |
| 73 | + let p: PP = CC() |
| 74 | + return p.returnOptionalSelf()?.returnOptionalSelf() |
| 75 | +} |
| 76 | + |
| 77 | +//===----------------------------------------------------------------------===// |
| 78 | +// testWitnessReturnOptionalIndirectSelf: Call to a witness method with an |
| 79 | +// existential self that can be type-propagated. sil-combine should bailout |
| 80 | +// since it does not propagate type substitutions on non-self arguments. It must |
| 81 | +// walk the Optional type to find Self in the non-self argument. |
| 82 | +// |
| 83 | +// Although sil-combine will not replace the self operand, it will still |
| 84 | +// rewrite the witness_method. The devirtualizer then handles the first call. |
| 85 | +//===----------------------------------------------------------------------===// |
| 86 | +protocol PPP { |
| 87 | + func returnsOptionalIndirect() -> Self? |
| 88 | +} |
| 89 | + |
| 90 | +struct S: PPP { |
| 91 | + func returnsOptionalIndirect() -> S? { |
| 92 | + return self |
| 93 | + } |
| 94 | +} |
| 95 | + |
| 96 | +struct SS: PPP { |
| 97 | + func returnsOptionalIndirect() -> SS? { |
| 98 | + return self |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +// The first apply has been devirtualized and inlined. The second remains unspecialized. |
| 103 | +// CHECK-LABEL: sil @$s37sil_combine_concrete_existential_ossa37testWitnessReturnOptionalIndirectSelfyyF : $@convention(thin) () -> () { |
| 104 | +// CHECK: [[O1:%.*]] = open_existential_addr immutable_access %{{.*}} : $*any PPP to $*@opened("{{.*}}", any PPP) Self |
| 105 | +// CHECK: switch_enum_addr %{{.*}} : $*Optional<@opened("{{.*}}", any PPP) Self>, case #Optional.some!enumelt: bb{{.*}}, case #Optional.none!enumelt: bb{{.*}} |
| 106 | +// CHECK: [[O2:%.*]] = open_existential_addr immutable_access %{{.*}} : $*any PPP to $*@opened("{{.*}}", any PPP) Self |
| 107 | +// CHECK: [[W:%.*]] = witness_method $@opened("{{.*}}", any PPP) Self, #PPP.returnsOptionalIndirect : <Self where Self : PPP> (Self) -> () -> Self?, [[O1]] : $*@opened("{{.*}}", any PPP) Self : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> |
| 108 | +// CHECK: apply [[W]]<@opened("{{.*}}", any PPP) Self>(%{{.*}}, [[O2]]) : $@convention(witness_method: PPP) <τ_0_0 where τ_0_0 : PPP> (@in_guaranteed τ_0_0) -> @out Optional<τ_0_0> |
| 109 | +// CHECK-LABEL: } // end sil function '$s37sil_combine_concrete_existential_ossa37testWitnessReturnOptionalIndirectSelfyyF' |
| 110 | +public func testWitnessReturnOptionalIndirectSelf() { |
| 111 | + let p: PPP = S() |
| 112 | + _ = p.returnsOptionalIndirect()?.returnsOptionalIndirect() |
| 113 | +} |
| 114 | + |
| 115 | +//===----------------------------------------------------------------------===// |
| 116 | +// testExtensionProtocolComposition: Call to a witness method with an |
| 117 | +// existential self that can be type-propagated. Handle an existential with |
| 118 | +// multiple conformances. |
| 119 | +// |
| 120 | +// Previously crashed with in SILCombiner::propagateConcreteTypeOfInitExistential |
| 121 | +// with assertion failed: (proto == Conformance.getRequirement()). |
| 122 | +// ===----------------------------------------------------------------------===// |
| 123 | +public protocol Q {} |
| 124 | + |
| 125 | +extension P where Self : Q { |
| 126 | + public func witnessComposition() {} |
| 127 | +} |
| 128 | + |
| 129 | +public class C_PQ: P & Q {} |
| 130 | + |
| 131 | +// testExtensionProtocolComposition(c:) |
| 132 | +// CHECK-LABEL: sil {{.*}}@$s37sil_combine_concrete_existential_ossa32testExtensionProtocolComposition1cyAA4C_PQC_tF : $@convention(thin) (@guaranteed C_PQ) -> () { |
| 133 | +// CHECK-NOT: init_existential_ref |
| 134 | +// CHECK-NOT: function_ref |
| 135 | +// CHECK-NOT: apply |
| 136 | +// CHECK: } // end sil function '$s37sil_combine_concrete_existential_ossa32testExtensionProtocolComposition1cyAA4C_PQC_tF' |
| 137 | +public func testExtensionProtocolComposition(c: C_PQ) { |
| 138 | + let pp: P & Q = c |
| 139 | + pp.witnessComposition() |
| 140 | +} |
0 commit comments