Skip to content

[6.2] CastOptimizer: don't assume dynamic casts from ObjectiveC classes to unrelated classes will fail #81251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion lib/SIL/Utils/DynamicCasts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions test/Interpreter/bridged_casts_folding.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
13 changes: 4 additions & 9 deletions test/SILOptimizer/cast_folding_no_bridging.sil
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
7 changes: 2 additions & 5 deletions test/SILOptimizer/cast_folding_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down