Skip to content

Commit 3cd90a1

Browse files
authored
Merge pull request #4465 from slavapestov/rjmccall-fix-dyncasts-volume-xxiv-3.0
Fix the cast optimizer to handle CF/NS bridging correctly,
2 parents 395e190 + f4d14b8 commit 3cd90a1

File tree

4 files changed

+199
-45
lines changed

4 files changed

+199
-45
lines changed

lib/SIL/DynamicCasts.cpp

Lines changed: 107 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,59 @@ static bool canDynamicallyBeOptionalType(CanType type) {
250250
&& !type.isAnyClassReferenceType();
251251
}
252252

253+
/// Given two class types, check whether there's a hierarchy relationship
254+
/// between them.
255+
static DynamicCastFeasibility
256+
classifyClassHierarchyCast(CanType source, CanType target) {
257+
// Upcast: if the target type statically matches a type in the
258+
// source type's hierarchy, this is a static upcast and the cast
259+
// will always succeed.
260+
if (target->isExactSuperclassOf(source, nullptr))
261+
return DynamicCastFeasibility::WillSucceed;
262+
263+
// Upcast: if the target type might dynamically match a type in the
264+
// source type's hierarchy, this might be an upcast, in which
265+
// case the cast might succeed.
266+
if (target->isBindableToSuperclassOf(source, nullptr))
267+
return DynamicCastFeasibility::MaySucceed;
268+
269+
// Downcast: if the source type might dynamically match a type in the
270+
// target type's hierarchy, this might be a downcast, in which case
271+
// the cast might succeed. Note that this also covers the case where
272+
// the source type statically matches a type in the target type's
273+
// hierarchy; since it's a downcast, the cast still at best might succeed.
274+
if (source->isBindableToSuperclassOf(target, nullptr))
275+
return DynamicCastFeasibility::MaySucceed;
276+
277+
// Otherwise, the classes are unrelated and the cast will fail (at least
278+
// on these grounds).
279+
return DynamicCastFeasibility::WillFail;
280+
}
281+
282+
static CanType getNSBridgedClassOfCFClass(Module *M, CanType type) {
283+
if (auto classDecl = type->getClassOrBoundGenericClass()) {
284+
if (classDecl->getForeignClassKind() == ClassDecl::ForeignKind::CFType) {
285+
if (auto bridgedAttr =
286+
classDecl->getAttrs().getAttribute<ObjCBridgedAttr>()) {
287+
auto bridgedClass = bridgedAttr->getObjCClass();
288+
// TODO: this should handle generic classes properly.
289+
if (!bridgedClass->isGenericContext()) {
290+
return bridgedClass->getDeclaredInterfaceType()->getCanonicalType();
291+
}
292+
}
293+
}
294+
}
295+
return CanType();
296+
}
297+
298+
static bool isCFBridgingConversion(Module *M, SILType sourceType,
299+
SILType targetType) {
300+
return (sourceType.getSwiftRValueType() ==
301+
getNSBridgedClassOfCFClass(M, targetType.getSwiftRValueType()) ||
302+
targetType.getSwiftRValueType() ==
303+
getNSBridgedClassOfCFClass(M, sourceType.getSwiftRValueType()));
304+
}
305+
253306
/// Try to classify the dynamic-cast relationship between two types.
254307
DynamicCastFeasibility
255308
swift::classifyDynamicCast(Module *M,
@@ -311,6 +364,30 @@ swift::classifyDynamicCast(Module *M,
311364
return DynamicCastFeasibility::MaySucceed;
312365
}
313366

367+
// Casts from AnyHashable.
368+
if (auto sourceStruct = dyn_cast<StructType>(source)) {
369+
if (sourceStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
370+
if (auto hashable = getHashableExistentialType(M)) {
371+
// Succeeds if Hashable can be cast to the target type.
372+
return classifyDynamicCastFromProtocol(M, hashable, target,
373+
isWholeModuleOpts);
374+
}
375+
}
376+
}
377+
378+
// Casts to AnyHashable.
379+
if (auto targetStruct = dyn_cast<StructType>(target)) {
380+
if (targetStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
381+
// Succeeds if the source type can be dynamically cast to Hashable.
382+
// Hashable is not actually a legal existential type right now, but
383+
// the check doesn't care about that.
384+
if (auto hashable = getHashableExistentialType(M)) {
385+
return classifyDynamicCastToProtocol(source, hashable,
386+
isWholeModuleOpts);
387+
}
388+
}
389+
}
390+
314391
// Metatype casts.
315392
if (auto sourceMetatype = dyn_cast<AnyMetatypeType>(source)) {
316393
auto targetMetatype = dyn_cast<AnyMetatypeType>(target);
@@ -369,7 +446,7 @@ swift::classifyDynamicCast(Module *M,
369446
// cast.
370447
if (source.getClassOrBoundGenericClass() &&
371448
target.getClassOrBoundGenericClass())
372-
return classifyDynamicCast(M, source, target, false, isWholeModuleOpts);
449+
return classifyClassHierarchyCast(source, target);
373450

374451
// Different structs cannot be cast to each other.
375452
if (source.getStructOrBoundGenericStruct() &&
@@ -386,7 +463,7 @@ swift::classifyDynamicCast(Module *M,
386463
// If we don't know any better, assume that the cast may succeed.
387464
return DynamicCastFeasibility::MaySucceed;
388465
}
389-
466+
390467
// Function casts.
391468
if (auto sourceFunction = dyn_cast<FunctionType>(source)) {
392469
if (auto targetFunction = dyn_cast<FunctionType>(target)) {
@@ -457,16 +534,25 @@ swift::classifyDynamicCast(Module *M,
457534
}
458535
}
459536

537+
// Try a hierarchy cast. If that isn't failure, we can report it.
538+
auto hierarchyResult = classifyClassHierarchyCast(source, target);
539+
if (hierarchyResult != DynamicCastFeasibility::WillFail)
540+
return hierarchyResult;
460541

461-
if (target->isExactSuperclassOf(source, nullptr))
462-
return DynamicCastFeasibility::WillSucceed;
463-
if (target->isBindableToSuperclassOf(source, nullptr))
464-
return DynamicCastFeasibility::MaySucceed;
465-
if (source->isBindableToSuperclassOf(target, nullptr))
466-
return DynamicCastFeasibility::MaySucceed;
542+
// As a backup, consider whether either type is a CF class type
543+
// with an NS bridged equivalent.
544+
CanType bridgedSource = getNSBridgedClassOfCFClass(M, source);
545+
CanType bridgedTarget = getNSBridgedClassOfCFClass(M, target);
467546

468-
// FIXME: bridged types, e.g. CF <-> NS (but not for metatypes).
469-
return DynamicCastFeasibility::WillFail;
547+
// If neither type qualifies, we're done.
548+
if (!bridgedSource && !bridgedTarget)
549+
return DynamicCastFeasibility::WillFail;
550+
551+
// Otherwise, map over to the bridged types and try to answer the
552+
// question there.
553+
if (bridgedSource) source = bridgedSource;
554+
if (bridgedTarget) target = bridgedTarget;
555+
return classifyDynamicCast(M, source, target, false, isWholeModuleOpts);
470556
}
471557

472558
// Casts from a class into a non-class can never succeed if the target must
@@ -599,30 +685,6 @@ swift::classifyDynamicCast(Module *M,
599685
}
600686
}
601687

602-
// Casts from AnyHashable.
603-
if (auto sourceStruct = dyn_cast<StructType>(source)) {
604-
if (sourceStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
605-
if (auto hashable = getHashableExistentialType(M)) {
606-
// Succeeds if Hashable can be cast to the target type.
607-
return classifyDynamicCastFromProtocol(M, hashable, target,
608-
isWholeModuleOpts);
609-
}
610-
}
611-
}
612-
613-
// Casts to AnyHashable.
614-
if (auto targetStruct = dyn_cast<StructType>(target)) {
615-
if (targetStruct->getDecl() == M->getASTContext().getAnyHashableDecl()) {
616-
// Succeeds if the source type can be dynamically cast to Hashable.
617-
// Hashable is not actually a legal existential type right now, but
618-
// the check doesn't care about that.
619-
if (auto hashable = getHashableExistentialType(M)) {
620-
return classifyDynamicCastToProtocol(source, hashable,
621-
isWholeModuleOpts);
622-
}
623-
}
624-
}
625-
626688
return DynamicCastFeasibility::WillFail;
627689
}
628690

@@ -685,9 +747,11 @@ namespace {
685747
SILModule &M;
686748
ASTContext &Ctx;
687749
SILLocation Loc;
750+
Module *SwiftModule;
688751
public:
689752
CastEmitter(SILBuilder &B, Module *swiftModule, SILLocation loc)
690-
: B(B), M(B.getModule()), Ctx(M.getASTContext()), Loc(loc) {}
753+
: B(B), M(B.getModule()), Ctx(M.getASTContext()), Loc(loc),
754+
SwiftModule(swiftModule) {}
691755

692756
Source emitTopLevel(Source source, Target target) {
693757
unsigned sourceOptDepth = getOptionalDepth(source.FormalType);
@@ -768,8 +832,8 @@ namespace {
768832
}
769833
assert(!target.FormalType.getAnyOptionalObjectType());
770834

771-
// The only other thing we return WillSucceed for currently is
772-
// an upcast.
835+
// The only other things we return WillSucceed for currently is
836+
// an upcast or CF/NS toll-free-bridging conversion.
773837
// FIXME: Upcasts between existential metatypes are not handled yet.
774838
// We should generate for it:
775839
// %openedSrcMetatype = open_existential srcMetatype
@@ -781,7 +845,12 @@ namespace {
781845
} else {
782846
value = getOwnedScalar(source, srcTL);
783847
}
784-
value = B.createUpcast(Loc, value, target.LoweredType.getObjectType());
848+
auto targetTy = target.LoweredType;
849+
if (isCFBridgingConversion(SwiftModule, targetTy, value->getType())) {
850+
value = B.createUncheckedRefCast(Loc, value, targetTy.getObjectType());
851+
} else {
852+
value = B.createUpcast(Loc, value, targetTy.getObjectType());
853+
}
785854
return putOwnedScalar(value, target);
786855
}
787856

stdlib/public/runtime/Casting.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2633,6 +2633,11 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
26332633
case MetadataKind::Class:
26342634
case MetadataKind::ObjCClassWrapper:
26352635
case MetadataKind::ForeignClass: {
2636+
// Casts to AnyHashable.
2637+
if (isAnyHashableType(targetType)) {
2638+
return _dynamicCastToAnyHashable(dest, src, srcType, targetType, flags);
2639+
}
2640+
26362641
#if SWIFT_OBJC_INTEROP
26372642
// If the target type is bridged to Objective-C, try to bridge.
26382643
if (auto targetBridgeWitness = findBridgeWitness(targetType)) {
@@ -2649,10 +2654,6 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
26492654
}
26502655
#endif
26512656

2652-
// Casts to AnyHashable.
2653-
if (isAnyHashableType(targetType)) {
2654-
return _dynamicCastToAnyHashable(dest, src, srcType, targetType, flags);
2655-
}
26562657
break;
26572658
}
26582659

@@ -2666,10 +2667,16 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
26662667
}
26672668
break;
26682669

2670+
case MetadataKind::Optional:
2671+
case MetadataKind::Enum:
2672+
// Casts to AnyHashable.
2673+
if (isAnyHashableType(targetType)) {
2674+
return _dynamicCastToAnyHashable(dest, src, srcType, targetType, flags);
2675+
}
2676+
break;
2677+
26692678
case MetadataKind::Existential:
26702679
case MetadataKind::ExistentialMetatype:
2671-
case MetadataKind::Enum:
2672-
case MetadataKind::Optional:
26732680
case MetadataKind::Function:
26742681
case MetadataKind::HeapLocalVariable:
26752682
case MetadataKind::HeapGenericLocalVariable:

test/1_stdlib/AnyHashableCasts.swift.gyb

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,37 @@ protocol Implemented {}
1515
protocol Unimplemented {}
1616
extension Int : Implemented {}
1717

18+
struct HashableStruct : Hashable {
19+
var value : Int
20+
static func ==(lhs: HashableStruct, rhs: HashableStruct) -> Bool {
21+
return lhs.value == rhs.value
22+
}
23+
var hashValue : Int { return value }
24+
}
25+
26+
class HashableClass : Hashable {
27+
var value : Int
28+
init(value v: Int) { self.value = v }
29+
static func ==(lhs: HashableClass, rhs: HashableClass) -> Bool {
30+
return lhs.value == rhs.value
31+
}
32+
var hashValue : Int { return value }
33+
}
34+
35+
enum HashableEnum : Hashable {
36+
case value(Int)
37+
static func ==(lhs: HashableEnum, rhs: HashableEnum) -> Bool {
38+
switch (lhs, rhs) {
39+
case (.value(let l), .value(let r)): return l == r
40+
}
41+
}
42+
var hashValue : Int {
43+
switch self {
44+
case .value(let v): return v
45+
}
46+
}
47+
}
48+
1849
func opaqueCast<T, U>(_ lhs: T, _ rhs: U.Type) -> U? {
1950
return lhs as? U
2051
}
@@ -39,7 +70,13 @@ testCases = [
3970
("5", "AnyHashable", "Unimplemented", False),
4071
("5", "Int", "AnyHashable", "5"),
4172
("5", "Any", "AnyHashable", "5"),
42-
("AnyHashable(5)", "Any", "Int", "5")
73+
("AnyHashable(5)", "Any", "Int", "5"),
74+
("HashableStruct(value: 5)", "HashableStruct", "AnyHashable",
75+
"AnyHashable(HashableStruct(value: 5))"),
76+
("HashableClass(value: 5)", "HashableClass", "AnyHashable",
77+
"AnyHashable(HashableClass(value: 5))"),
78+
("HashableEnum.value(5)", "HashableEnum", "AnyHashable",
79+
"AnyHashable(HashableEnum.value(5))"),
4380
]
4481
}%
4582

test/SILOptimizer/cast_folding_no_bridging.sil

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,44 @@ bb2(%14 : $NSSet):
5959
bb3:
6060
br bb2(%1 : $NSSet)
6161
}
62+
63+
sil @fail : $@convention(thin) () -> Never
64+
65+
// CHECK-LABEL: sil @testCFToObjC
66+
// CHECK: bb0(
67+
// CHECK-NEXT: [[T0:%.*]] = load %1 : $*CFString
68+
// CHECK-NEXT: [[T1:%.*]] = unchecked_ref_cast [[T0]] : $CFString to $NSString
69+
// CHECK-NEXT: store [[T1]] to %0 : $*NSString
70+
sil @testCFToObjC : $@convention(thin) (@in CFString) -> @out NSString {
71+
bb0(%0 : $*NSString, %1 : $*CFString):
72+
checked_cast_addr_br take_always CFString in %1 : $*CFString to NSString in %0 : $*NSString, bb1, bb2
73+
74+
bb1:
75+
%ret = tuple ()
76+
return %ret : $()
77+
78+
bb2:
79+
%fn = function_ref @fail : $@convention(thin) () -> Never
80+
apply %fn() : $@convention(thin) () -> Never
81+
unreachable
82+
}
83+
84+
// CHECK-LABEL: sil @testCFToSwift
85+
// CHECK: bb0(
86+
// CHECK-NEXT: [[T0:%.*]] = load %1 : $*CFString
87+
// CHECK-NEXT: [[T1:%.*]] = unchecked_ref_cast [[T0]] : $CFString to $NSString
88+
// CHECK: [[FN:%.*]] = function_ref @_TTWSSs21_ObjectiveCBridgeable10FoundationZFS_34_conditionallyBridgeFromObjectiveCfTwx15_ObjectiveCType6resultRGSqx__Sb : $@convention(witness_method) (@owned NSString, @inout Optional<String>, @thick String.Type) -> Bool
89+
// CHECK: apply [[FN]]([[T1]], {{.*}}, {{.*}})
90+
sil @testCFToSwift : $@convention(thin) (@in CFString) -> @out String {
91+
bb0(%0 : $*String, %1 : $*CFString):
92+
checked_cast_addr_br take_always CFString in %1 : $*CFString to String in %0 : $*String, bb1, bb2
93+
94+
bb1:
95+
%ret = tuple ()
96+
return %ret : $()
97+
98+
bb2:
99+
%fn = function_ref @fail : $@convention(thin) () -> Never
100+
apply %fn() : $@convention(thin) () -> Never
101+
unreachable
102+
}

0 commit comments

Comments
 (0)