mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
CastOptimizer: don't assume dynamic casts from ObjectiveC classes to unrelated classes will fail
In case of ObjectiveC classes, the runtime type can differ from its declared type. Therefore a cast between (compile-time) unrelated classes may succeed at runtime. rdar://149810124
This commit is contained in:
@@ -741,7 +741,10 @@ swift::classifyDynamicCast(SILFunction *function,
|
||||
if (targetClass->isSuperclassOf(sourceClass))
|
||||
return DynamicCastFeasibility::WillSucceed;
|
||||
|
||||
return DynamicCastFeasibility::WillFail;
|
||||
// In case of ObjectiveC classes, the runtime type can differ from its
|
||||
// declared type. Therefore a cast between (compile-time) unrelated
|
||||
// classes may succeed at runtime.
|
||||
return DynamicCastFeasibility::MaySucceed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -750,6 +753,12 @@ swift::classifyDynamicCast(SILFunction *function,
|
||||
if (hierarchyResult != DynamicCastFeasibility::WillFail)
|
||||
return hierarchyResult;
|
||||
|
||||
// In case of ObjectiveC classes, the runtime type can differ from its
|
||||
// declared type. Therefore a cast between (compile-time) unrelated
|
||||
// classes may succeed at runtime.
|
||||
if (sourceClass->hasClangNode())
|
||||
return DynamicCastFeasibility::MaySucceed;
|
||||
|
||||
// As a backup, consider whether either type is a CF class type
|
||||
// with an NS bridged equivalent.
|
||||
CanType bridgedSource = getNSBridgedClassOfCFClass(source);
|
||||
|
||||
@@ -58,7 +58,7 @@ Tests.test("NSString => Array<Int>. Crashing test case") {
|
||||
// CHECK: [ OK ] BridgedCastFolding.NSString => Array<Int>. Crashing test case
|
||||
|
||||
// CHECK-OPT-LABEL: [ RUN ] BridgedCastFolding.NSString => Array<Int>. Crashing test case
|
||||
// CHECK-OPT: stderr>>> OK: saw expected "crashed: sig{{ill|trap}}"
|
||||
// CHECK-OPT: stderr>>> OK: saw expected "crashed: sigabrt"
|
||||
// CHECK-OPT: [ OK ] BridgedCastFolding.NSString => Array<Int>. Crashing test case
|
||||
expectCrashLater()
|
||||
do {
|
||||
@@ -130,7 +130,7 @@ Tests.test("NSNumber (Int) -> String. Crashing test.") {
|
||||
// CHECK: [ OK ] BridgedCastFolding.NSNumber (Int) -> String. Crashing test.
|
||||
|
||||
// CHECK-OPT-LABEL: [ RUN ] BridgedCastFolding.NSNumber (Int) -> String. Crashing test.
|
||||
// CHECK-OPT: stderr>>> OK: saw expected "crashed: sig{{ill|trap}}"
|
||||
// CHECK-OPT: stderr>>> OK: saw expected "crashed: sigabrt"
|
||||
// CHECK-OPT: [ OK ] BridgedCastFolding.NSNumber (Int) -> String. Crashing test.
|
||||
expectCrashLater()
|
||||
do {
|
||||
@@ -393,7 +393,7 @@ Tests.test("String -> NSNumber. Crashing Test Case") {
|
||||
// CHECK: [ OK ] BridgedCastFolding.String -> NSNumber. Crashing Test Case
|
||||
|
||||
// CHECK-OPT-LABEL: [ RUN ] BridgedCastFolding.String -> NSNumber. Crashing Test Case
|
||||
// CHECK-OPT: stderr>>> OK: saw expected "crashed: sig{{ill|trap}}"
|
||||
// CHECK-OPT: stderr>>> OK: saw expected "crashed: sigabrt"
|
||||
// CHECK-OPT: [ OK ] BridgedCastFolding.String -> NSNumber. Crashing Test Case
|
||||
expectCrashLater()
|
||||
do {
|
||||
|
||||
@@ -64,10 +64,8 @@ bb3:
|
||||
sil @fail : $@convention(thin) () -> Never
|
||||
|
||||
// CHECK-LABEL: sil {{.*}}@testCFToObjC
|
||||
// CHECK: bb0(
|
||||
// CHECK-NEXT: [[T0:%.*]] = load %1 : $*CFString
|
||||
// CHECK-NEXT: [[T1:%.*]] = unchecked_ref_cast [[T0]] : $CFString to $NSString
|
||||
// CHECK-NEXT: store [[T1]] to %0 : $*NSString
|
||||
// CHECK: checked_cast_addr_br
|
||||
// CHECK: } // end sil function 'testCFToObjC'
|
||||
sil @testCFToObjC : $@convention(thin) (@in CFString) -> @out NSString {
|
||||
bb0(%0 : $*NSString, %1 : $*CFString):
|
||||
checked_cast_addr_br take_always CFString in %1 : $*CFString to NSString in %0 : $*NSString, bb1, bb2
|
||||
@@ -83,11 +81,8 @@ bb2:
|
||||
}
|
||||
|
||||
// CHECK-LABEL: sil {{.*}}@testCFToSwift
|
||||
// CHECK: bb0(
|
||||
// CHECK-NEXT: [[T0:%.*]] = load %1 : $*CFString
|
||||
// CHECK-NEXT: [[T1:%.*]] = unchecked_ref_cast [[T0]] : $CFString to $NSString
|
||||
// CHECK: [[FN:%.*]] = function_ref @$sSS10FoundationE34_conditionallyBridgeFromObjectiveC_6resultSbSo8NSStringC_SSSgztFZ : $@convention(method) (@guaranteed NSString, @inout Optional<String>, @thin String.Type) -> Bool
|
||||
// CHECK: apply [[FN]]([[T1]], {{.*}}, {{.*}})
|
||||
// CHECK: checked_cast
|
||||
// CHECK: } // end sil function 'testCFToSwift'
|
||||
sil @testCFToSwift : $@convention(thin) (@in CFString) -> @out String {
|
||||
bb0(%0 : $*String, %1 : $*CFString):
|
||||
checked_cast_addr_br take_always CFString in %1 : $*CFString to String in %0 : $*String, bb1, bb2
|
||||
|
||||
@@ -77,12 +77,9 @@ public func castObjCToSwift<T>(_ t: T) -> Int {
|
||||
return t as! Int
|
||||
}
|
||||
|
||||
// Check that compiler understands that this cast always fails
|
||||
// CHECK-LABEL: sil [noinline] {{.*}}@$s17cast_folding_objc37testFailingBridgedCastFromObjCtoSwiftySiSo8NSStringCF
|
||||
// CHECK: [[ONE:%[0-9]+]] = integer_literal $Builtin.Int1, -1
|
||||
// CHECK: cond_fail [[ONE]] : $Builtin.Int1, "failed cast"
|
||||
// CHECK-NEXT: unreachable
|
||||
// CHECK-NEXT: }
|
||||
// CHECK: unconditional_checked_cast %0 : $NSString to NSNumber
|
||||
// CHECK: } // end sil function '$s17cast_folding_objc37testFailingBridgedCastFromObjCtoSwiftySiSo8NSStringCF'
|
||||
@inline(never)
|
||||
public func testFailingBridgedCastFromObjCtoSwift(_ ns: NSString) -> Int {
|
||||
return castObjCToSwift(ns)
|
||||
|
||||
Reference in New Issue
Block a user