Skip to content

Commit c078479

Browse files
committed
[Casting] Make more casts look inside __SwiftValue
The following sequence of casts would previously succeed ``` struct S {} let s = S() as AnyObject s as? NSObject ``` The final cast here should fail, since `S` clearly is not a subclass of NSObject. But it would previously succeed because the `as AnyObject` would package the struct into an ObjC-compatible `__SwiftValue` class. This latter is an NSObject subclass. This bug was fixed in the main `swift_dynamicCast` runtime function some time ago, but not in the `swift_dynamicCastObjCClass` which is chosen by IRGen to optimize casts to ObjC class types. This PR changes the latter to test for `__SwiftValue` and fall back to the former in that case in order to get the correct handling. Falling back to `swift_dynamicCast` also ensures that the contents of the `__SwiftValue` are correctly unwrapped/bridged/etc as necessary to fully support Swift casting semantics. Resolves: rdar://111422725 TODO: I've left an XFAILed test here about the behavior of `type(of:)` with `__SwiftValue` boxes.
1 parent df48b60 commit c078479

File tree

2 files changed

+31
-0
lines changed

2 files changed

+31
-0
lines changed

stdlib/public/runtime/SwiftObject.mm

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,22 @@ static bool isObjCForUnownedReference(void *value) {
10931093
if (object == nullptr)
10941094
return nullptr;
10951095

1096+
if ([id_const_cast(object) isKindOfClass:[__SwiftValue class]]) {
1097+
// Source is a `__SwiftValue` container
1098+
// Unwrap, then use the most general casting machine to do the heavy lifting
1099+
auto typeValue = getValueFromSwiftValue(reinterpret_cast<__SwiftValue *>(object));
1100+
const void *result = nullptr;
1101+
if (swift_dynamicCast(reinterpret_cast<OpaqueValue *>(&result),
1102+
const_cast<OpaqueValue *>(typeValue.second),
1103+
typeValue.first,
1104+
targetType,
1105+
DynamicCastFlags::TakeOnSuccess)) {
1106+
return result;
1107+
} else {
1108+
return nullptr;
1109+
}
1110+
}
1111+
10961112
if ([id_const_cast(object) isKindOfClass:class_const_cast(targetType)]) {
10971113
return object;
10981114
}

test/Casting/Casts.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,4 +1064,19 @@ CastsTests.test("Don't put AnyHashable inside AnyObject") {
10641064
expectTrue(a === d)
10651065
}
10661066

1067+
CastsTests.test("__SwiftValue should not be obvious to `is`") {
1068+
struct S {}
1069+
let s = S() as AnyObject
1070+
expectFalse(s is NSObject)
1071+
}
1072+
1073+
CastsTests.test("type(of:) should look through __SwiftValue")
1074+
.xfail(.always("Known to be broken"))
1075+
.code {
1076+
struct S {}
1077+
let s = S() as AnyObject
1078+
let t = "\(type(of: s))"
1079+
expectEqual(t, "S") // Fails: currently says `__SwiftValue`
1080+
}
1081+
10671082
runAllTests()

0 commit comments

Comments
 (0)