Skip to content

Commit ab8ab48

Browse files
authored
Merge pull request #10313 from swiftix/swift-4.0-branch
[cast-optimizer] Fix casting of P.Protocol to P.Type
2 parents bf84933 + 699e044 commit ab8ab48

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

lib/SIL/DynamicCasts.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,22 @@ swift::classifyDynamicCast(ModuleDecl *M,
390390
sourceMetatype.isAnyExistentialType())
391391
return DynamicCastFeasibility::WillSucceed;
392392

393+
// If the source and target are the same existential type, but the source is
394+
// P.Protocol and the dest is P.Type, then we need to consider whether the
395+
// protocol is self-conforming.
396+
// The only cases where a protocol self-conforms are objc protocols, but
397+
// we're going to expect P.Type to hold a class object. And this case
398+
// doesn't matter since for a self-conforming protocol type there can't be
399+
// any type-level methods.
400+
// Thus we consider this kind of cast to always fail. The only exception
401+
// from this rule is when the target is Any.Type, because *.Protocol
402+
// can always be casted to Any.Type.
403+
if (source->isAnyExistentialType() && isa<MetatypeType>(sourceMetatype) &&
404+
isa<ExistentialMetatypeType>(targetMetatype)) {
405+
return target->isAny() ? DynamicCastFeasibility::WillSucceed
406+
: DynamicCastFeasibility::WillFail;
407+
}
408+
393409
if (targetMetatype.isAnyExistentialType() &&
394410
(isa<ProtocolType>(target) || isa<ProtocolCompositionType>(target))) {
395411
auto Feasibility = classifyDynamicCastToProtocol(source,

test/SILOptimizer/cast_folding.swift

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// which returns either true or false, i.e. all type checks should folded statically.
88

99
public protocol P {}
10+
public protocol R {}
1011

1112
protocol Q: P {}
1213

@@ -950,6 +951,87 @@ public func test42(_ p: P) -> Bool {
950951
return cast42(p)
951952
}
952953

954+
// CHECK-LABEL: sil [noinline] @{{.*}}test43{{.*}}
955+
// CHECK: bb0
956+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
957+
// CHECK-NEXT: %1 = struct $Bool
958+
// CHECK-NEXT: return %1
959+
@inline(never)
960+
public func test43() -> Bool {
961+
return P.self is Any.Type
962+
}
963+
964+
// CHECK-LABEL: sil [noinline] @{{.*}}test44{{.*}}
965+
// CHECK: bb0
966+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
967+
// CHECK-NEXT: %1 = struct $Bool
968+
// CHECK-NEXT: return %1
969+
@inline(never)
970+
public func test44() -> Bool {
971+
return Any.self is Any.Type
972+
}
973+
974+
// CHECK-LABEL: sil [noinline] @{{.*}}test45{{.*}}
975+
// CHECK: bb0
976+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
977+
// CHECK-NEXT: %1 = struct $Bool
978+
// CHECK-NEXT: return %1
979+
@inline(never)
980+
public func test45() -> Bool {
981+
return (P & R).self is Any.Type
982+
}
983+
984+
// CHECK-LABEL: sil [noinline] @{{.*}}test46{{.*}}
985+
// CHECK: bb0
986+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
987+
// CHECK-NEXT: %1 = struct $Bool
988+
// CHECK-NEXT: return %1
989+
@inline(never)
990+
public func test46() -> Bool {
991+
return AnyObject.self is Any.Type
992+
}
993+
994+
// CHECK-LABEL: sil [noinline] @{{.*}}test47{{.*}}
995+
// CHECK: bb0
996+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, -1
997+
// CHECK-NEXT: %1 = struct $Bool
998+
// CHECK-NEXT: return %1
999+
@inline(never)
1000+
public func test47() -> Bool {
1001+
return Any.Type.self is Any.Type
1002+
}
1003+
1004+
// CHECK-LABEL: sil [noinline] @{{.*}}test48{{.*}}
1005+
// CHECK: bb0
1006+
// CHECK-NEXT: %0 = integer_literal $Builtin.Int1, 0
1007+
// CHECK-NEXT: %1 = struct $Bool
1008+
// CHECK-NEXT: return %1
1009+
@inline(never)
1010+
public func test48() -> Bool {
1011+
return Any.Type.self is Any.Type.Type
1012+
}
1013+
1014+
func cast<U, V>(_ u: U.Type) -> V? {
1015+
return u as? V
1016+
}
1017+
1018+
// CHECK-LABEL: sil [noinline] @{{.*}}testCastAnyObjectProtocolTo{{.*}}Type
1019+
// CHECK: %0 = enum $Optional{{.*}}, #Optional.none!enumelt
1020+
// CHECK-NEXT: return %0
1021+
@inline(never)
1022+
public func testCastAnyObjectProtocolToAnyObjectType() -> AnyObject.Type? {
1023+
return cast(AnyObject.self)
1024+
}
1025+
1026+
// CHECK-LABEL: // testCastProtocolTypeProtocolToProtocolTypeType
1027+
// CHECK: sil [noinline] @{{.*}}testCastProtocol{{.*}}$@convention(thin) () -> Optional<@thick P.Type.Type>
1028+
// CHECK: %0 = enum $Optional{{.*}}, #Optional.none!enumelt
1029+
// CHECK-NEXT: return %0
1030+
@inline(never)
1031+
public func testCastProtocolTypeProtocolToProtocolTypeType() -> P.Type.Type? {
1032+
return P.Type.self as? P.Type.Type
1033+
}
1034+
9531035
print("test0=\(test0())")
9541036

9551037
print("test1=\(test1())")

test/SILOptimizer/cast_folding_objc.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,38 @@ public func testCastEveryToNonClassType<T>(_ o: T) -> Int.Type {
196196
return o as! Int.Type
197197
}
198198

199+
func cast<U, V>(_ u: U.Type) -> V? {
200+
return u as? V
201+
}
202+
203+
public protocol P {
204+
}
205+
206+
// Any casts from P.Protocol to P.Type should fail.
207+
@inline(never)
208+
public func testCastPProtocolToPType() -> ObjCP.Type? {
209+
return cast(ObjCP.self)
210+
}
211+
212+
@objc
213+
public protocol ObjCP {
214+
}
215+
216+
@inline(never)
217+
public func testCastObjCPProtocolToObjCPType() -> ObjCP.Type? {
218+
return cast(ObjCP.self)
219+
}
220+
221+
@inline(never)
222+
public func testCastProtocolCompositionProtocolToProtocolCompositionType() -> (P & ObjCP).Type? {
223+
return cast((P & ObjCP).self)
224+
}
225+
226+
@inline(never)
227+
public func testCastProtocolCompositionProtocolToProtocolType () -> P.Type? {
228+
return (P & ObjCP).self as? P.Type
229+
}
230+
199231
print("test0=\(test0())")
200232

201233

@@ -241,7 +273,17 @@ print("test0=\(test0())")
241273
// CHECK-LABEL: sil [noinline] @{{.*}}testCastEveryToNonClassType
242274
// CHECK: unconditional_checked_cast_addr
243275

276+
// CHECK-LABEL: sil [noinline] @{{.*}}testCastPProtocolToPType
277+
// CHECK: %0 = enum $Optional{{.*}}, #Optional.none!enumelt
278+
// CHECK-NEXT: return %0
279+
280+
// CHECK-LABEL: sil [noinline] @{{.*}}testCastObjCPProtocolTo{{.*}}PType
281+
// CHECK: %0 = enum $Optional{{.*}}, #Optional.none!enumelt
282+
// CHECK-NEXT: return %0
244283

284+
// CHECK-LABEL: sil [noinline] @{{.*}}testCastProtocolComposition{{.*}}Type
285+
// CHECK: %0 = enum $Optional{{.*}}, #Optional.none!enumelt
286+
// CHECK-NEXT: return %0
245287

246288
// Check that compiler understands that this cast always succeeds.
247289
// Since it is can be statically proven that NSString is bridgeable to String,

0 commit comments

Comments
 (0)