Skip to content

Commit 698ff60

Browse files
authored
Merge pull request #41926 from CodaFi/caster-oil
Conservative Dynamic Casts to/from Parameterized Protocols
2 parents 458ce70 + 6438c7e commit 698ff60

File tree

2 files changed

+98
-0
lines changed

2 files changed

+98
-0
lines changed

lib/SIL/Utils/DynamicCasts.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ static bool canClassOrSuperclassesHaveUnknownSubclasses(ClassDecl *CD,
7878
return false;
7979
}
8080

81+
static CanType unwrapExistential(CanType e) {
82+
assert(e.isExistentialType());
83+
84+
if (auto et = dyn_cast<ExistentialType>(e))
85+
return et.getConstraintType();
86+
87+
return e;
88+
}
89+
8190
/// Try to classify a conversion from non-existential type
8291
/// into an existential type by performing a static check
8392
/// of protocol conformances if it is possible.
@@ -94,6 +103,14 @@ classifyDynamicCastToProtocol(ModuleDecl *M, CanType source, CanType target,
94103
if (!TargetProtocol)
95104
return DynamicCastFeasibility::MaySucceed;
96105

106+
// If the target is a parameterized protocol type, conformsToProtocol
107+
// is insufficient to prove the feasibility of the cast as it does not
108+
// check the additional requirements.
109+
// FIXME: This is a weak predicate that doesn't take into account
110+
// class compositions - since any C & P<T> doesn't work yet anyways.
111+
if (isa<ParameterizedProtocolType>(unwrapExistential(target)))
112+
return DynamicCastFeasibility::MaySucceed;
113+
97114
// If conformsToProtocol returns a valid conformance, then all requirements
98115
// were proven by the type checker.
99116
if (M->conformsToProtocol(source, TargetProtocol))
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// RUN: %target-swift-frontend %s -emit-sil -enable-parameterized-protocol-types -O -o - | %FileCheck %s
2+
3+
public protocol P<T> {}
4+
public protocol Q: P where T == Int {}
5+
public struct X: Q {
6+
public typealias T = Int
7+
}
8+
public struct Y<T>: P {}
9+
extension Y: Q where T == Int {}
10+
11+
@_optimize(none)
12+
func use<T>(_ t: T) {}
13+
14+
// CHECK-LABEL: sil @$s35cast_folding_parameterized_protocol23concrete_to_existentialyyAA1XV_AA1YVyxGAFySiGtlF : $@convention(thin) <T> (X, Y<T>, Y<Int>) -> ()
15+
public func concrete_to_existential<T>(_ x: X, _ yt: Y<T>, _ yi: Y<Int>) {
16+
// CHECK:{{%.*}} = init_existential_addr %6 : $*P, $X
17+
use(x as! any P)
18+
// CHECK: unconditional_checked_cast_addr X in {{%.*}} : $*X to P<T> in {{%.*}} : $*P<T>
19+
use(x as! any P<T>)
20+
// CHECK: unconditional_checked_cast_addr X in {{%.*}} : $*X to P<Int> in {{%.*}} : $*P<Int>
21+
use(x as! any P<Int>)
22+
// CHECK: unconditional_checked_cast_addr X in {{%.*}} : $*X to P<String> in {{%.*}} : $*P<String>
23+
use(x as! any P<String>)
24+
// CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*Q, $X
25+
use(x as! any Q)
26+
27+
// CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*P, $Y<T>
28+
use(yt as! any P)
29+
// CHECK: unconditional_checked_cast_addr Y<T> in {{%.*}} : $*Y<T> to P<T> in {{%.*}} : $*P<T>
30+
use(yt as! any P<T>)
31+
// CHECK: unconditional_checked_cast_addr Y<T> in {{%.*}} : $*Y<T> to P<Int> in {{%.*}} : $*P<Int>
32+
use(yt as! any P<Int>)
33+
// CHECK: unconditional_checked_cast_addr Y<T> in {{%.*}} : $*Y<T> to P<String> in {{%.*}} : $*P<String>
34+
use(yt as! any P<String>)
35+
// CHECK: unconditional_checked_cast_addr Y<T> in {{%.*}} : $*Y<T> to Q in {{%.*}} : $*Q
36+
use(yt as! any Q)
37+
38+
// CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*P, $Y<Int>
39+
use(yi as! any P)
40+
// CHECK: unconditional_checked_cast_addr Y<Int> in {{%.*}} : $*Y<Int> to P<T> in {{%.*}} : $*P<T>
41+
use(yi as! any P<T>)
42+
// CHECK: unconditional_checked_cast_addr Y<Int> in {{%.*}} : $*Y<Int> to P<Int> in {{%.*}} : $*P<Int>
43+
use(yi as! any P<Int>)
44+
// CHECK: unconditional_checked_cast_addr Y<Int> in {{%.*}} : $*Y<Int> to P<String> in {{%.*}} : $*P<String>
45+
use(yi as! any P<String>)
46+
// CHECK: {{%.*}} = init_existential_addr {{%.*}} : $*Q, $Y<Int>
47+
use(yi as! any Q)
48+
}
49+
50+
// CHECK-LABEL: sil @$s35cast_folding_parameterized_protocol23existential_to_concreteyyxm_AA1P_pyxXPtlF : $@convention(thin) <T> (@thick T.Type, @in_guaranteed P<T>) -> ()
51+
public func existential_to_concrete<T>(_: T.Type, _ p: any P<T>) {
52+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to X in {{%.*}} : $*X
53+
_ = p as! X
54+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to Y<T> in {{%.*}} : $*Y<T>
55+
_ = p as! Y<T>
56+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to Y<Int> in {{%.*}} : $*Y<Int>
57+
_ = p as! Y<Int>
58+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to Y<String> in {{%.*}} : $*Y<String>
59+
_ = p as! Y<String>
60+
}
61+
62+
// CHECK-LABEL: sil @$s35cast_folding_parameterized_protocol015existential_to_E0yyAA1P_pyxXP_AA1Q_ptlF : $@convention(thin) <T> (@in_guaranteed P<T>, @in_guaranteed Q) -> ()
63+
public func existential_to_existential<T>(_ p: any P<T>, _ q: any Q) {
64+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to Q in {{%.*}} : $*Q
65+
_ = p as! any Q
66+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to P in {{%.*}} : $*P
67+
_ = p as! any P
68+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to P<Int> in {{%.*}} : $*P<Int>
69+
_ = p as! any P<Int>
70+
// CHECK: unconditional_checked_cast_addr P<T> in {{%.*}} : $*P<T> to P<String> in {{%.*}} : $*P<String>
71+
_ = p as! any P<String>
72+
73+
// CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P in {{%.*}} : $*P
74+
_ = q as! any P
75+
// CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P<T> in {{%.*}} : $*P<T>
76+
_ = q as! any P<T>
77+
// CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P<Int> in {{%.*}} : $*P<Int>
78+
_ = q as! any P<Int>
79+
// CHECK: unconditional_checked_cast_addr Q in {{%.*}} : $*Q to P<String> in {{%.*}} : $*P<String>
80+
_ = q as! any P<String>
81+
}

0 commit comments

Comments
 (0)