Skip to content

Commit cfee4d9

Browse files
author
Greg Parker
authored
[runtime] Fix some casts of _SwiftValue (#8457)
* [runtime] Fix some casts of _SwiftValue. * Allow _SwiftValue to be cast to NSObject by yielding the box object itself. * Failed casts from NSDictionary containing _SwiftValue should not crash. SR-4306, rdar://31197066
1 parent 1bdf679 commit cfee4d9

File tree

3 files changed

+99
-39
lines changed

3 files changed

+99
-39
lines changed

stdlib/private/StdlibUnittest/LifetimeTracked.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ public final class LifetimeTracked {
4141
public var serialNumber: Int = 0
4242
}
4343

44+
extension LifetimeTracked : Equatable {
45+
public static func == (x: LifetimeTracked, y: LifetimeTracked) -> Bool {
46+
return x.value == y.value
47+
}
48+
}
49+
50+
extension LifetimeTracked : Hashable {
51+
public var hashValue: Int {
52+
return value
53+
}
54+
}
55+
4456
extension LifetimeTracked : Strideable {
4557
public func distance(to other: LifetimeTracked) -> Int {
4658
return self.value.distance(to: other.value)
@@ -58,10 +70,6 @@ extension LifetimeTracked : CustomStringConvertible {
5870
}
5971
}
6072

61-
public func == (x: LifetimeTracked, y: LifetimeTracked) -> Bool {
62-
return x.value == y.value
63-
}
64-
6573
public func < (x: LifetimeTracked, y: LifetimeTracked) -> Bool {
6674
return x.value < y.value
6775
}

stdlib/public/runtime/Casting.cpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2195,6 +2195,10 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
21952195
const Metadata *srcType,
21962196
const Metadata *targetType,
21972197
DynamicCastFlags flags) {
2198+
// These flag combinations are not handled here.
2199+
assert(!(flags & DynamicCastFlags::Unconditional));
2200+
assert(!(flags & DynamicCastFlags::DestroyOnFailure));
2201+
21982202
// Swift type should be AnyObject or a class type.
21992203
if (!srcType->isAnyClass()) {
22002204
auto existential = dyn_cast<ExistentialTypeMetadata>(srcType);
@@ -2234,7 +2238,8 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
22342238

22352239
// Maybe we can cast the boxed value to our destination type somehow.
22362240
auto innerFlags = flags - DynamicCastFlags::TakeOnSuccess
2237-
- DynamicCastFlags::DestroyOnFailure;
2241+
- DynamicCastFlags::DestroyOnFailure
2242+
- DynamicCastFlags::Unconditional;
22382243
if (swift_dynamicCast(dest, const_cast<OpaqueValue*>(boxedValue),
22392244
boxedType, targetType, innerFlags)) {
22402245
// Release the box if we need to.
@@ -2525,10 +2530,19 @@ bool swift::swift_dynamicCast(OpaqueValue *dest, OpaqueValue *src,
25252530
return unwrapResult.success;
25262531

25272532
#if SWIFT_OBJC_INTEROP
2528-
// A class or AnyObject reference may point at a boxed _SwiftValue.
2529-
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
2530-
targetType, flags)) {
2531-
return true;
2533+
// A class or AnyObject reference may point to a _SwiftValue box.
2534+
{
2535+
auto innerFlags = flags - DynamicCastFlags::Unconditional
2536+
- DynamicCastFlags::DestroyOnFailure;
2537+
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
2538+
targetType, innerFlags)) {
2539+
// TakeOnSuccess was handled inside tryDynamicCastBoxedSwiftValue().
2540+
return true;
2541+
} else {
2542+
// Couldn't cast boxed value to targetType.
2543+
// Fall through and try to cast the _SwiftValue box itself to targetType.
2544+
// (for example, casting _SwiftValue to NSObject will be successful)
2545+
}
25322546
}
25332547
#endif
25342548

test/stdlib/BridgeIdAsAny.swift.gyb

Lines changed: 68 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,21 @@ func wantonlyWrapInAny<T>(_ x: T) -> Any {
2020
extension LifetimeTracked: Error {}
2121
extension String: Error {}
2222

23-
struct KnownUnbridged: Equatable, Error {
23+
struct KnownUnbridged: Equatable, Hashable, Error {
2424
var x, y: LifetimeTracked
2525

2626
init() {
2727
x = LifetimeTracked(17)
2828
y = LifetimeTracked(38)
2929
}
30-
}
31-
func ==(a: KnownUnbridged, b: KnownUnbridged) -> Bool {
32-
return a.x === b.x && a.y === b.y
30+
31+
public static func ==(a: KnownUnbridged, b: KnownUnbridged) -> Bool {
32+
return a.x === b.x && a.y === b.y
33+
}
34+
35+
public var hashValue: Int {
36+
return x.hashValue ^ y.hashValue
37+
}
3338
}
3439

3540
struct KnownUnbridgedWithDescription: CustomStringConvertible,
@@ -96,22 +101,22 @@ func boxedTypeRoundTripsThroughDynamicCasting(original: KnownUnbridged,
96101

97102
func tupleCanBeDynamicallyCast(original: (Int, String),
98103
bridged: AnyObject) {
99-
expectTrue(original == bridged as! (Int, String))
104+
expectTrue(original == (bridged as! (Int, String)))
100105
}
101106
func metatypeCanBeDynamicallyCast(original: Int.Type,
102107
bridged: AnyObject) {
103-
expectTrue(original == bridged as! Int.Type)
104-
expectTrue(original == bridged as! Any.Type)
108+
expectTrue(original == (bridged as! Int.Type))
109+
expectTrue(original == (bridged as! Any.Type))
105110
}
106111
func metatypeCanBeDynamicallyCast(original: CFString.Type,
107112
bridged: AnyObject) {
108-
expectTrue(original == bridged as! CFString.Type)
109-
expectTrue(original == bridged as! Any.Type)
113+
expectTrue(original == (bridged as! CFString.Type))
114+
expectTrue(original == (bridged as! Any.Type))
110115
}
111116
func metatypeCanBeDynamicallyCastGenerically<T>(original: T.Type,
112117
bridged: AnyObject) {
113-
expectTrue(original == bridged as! T.Type)
114-
expectTrue(original == bridged as! Any.Type)
118+
expectTrue(original == (bridged as! T.Type))
119+
expectTrue(original == (bridged as! Any.Type))
115120
}
116121

117122

@@ -151,33 +156,66 @@ protocol P {}
151156
// interesting bridging cases in different kinds of existential container.
152157
%{
153158
testCases = [
154-
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True),
155-
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True),
156-
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True),
157-
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False),
158-
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False),
159-
("generic metatype", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False),
160-
("CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCast", False),
161-
("generic CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False),
162-
("class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False),
163-
("objc metatype", 'NSObject.self', "classMetatypePreservesIdentity", False),
164-
("protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False),
165-
("objc protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False),
166-
("objc protocol composition", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False),
167-
("mixed protocol composition", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False),
168-
("generic class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False),
169-
("generic objc metatype", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False),
170-
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False),
159+
# testName valueExpr testFunc conformsToError conformsToHashable
160+
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True),
161+
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True),
162+
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True, True),
163+
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False, False),
164+
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False, False),
165+
("generic metatype", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
166+
("CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCast", False, False),
167+
("generic CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
168+
("class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False, False),
169+
("objc metatype", 'NSObject.self', "classMetatypePreservesIdentity", False, False),
170+
("protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
171+
("objc protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False, False),
172+
("objc protocol composition", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
173+
("mixed protocol composition", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
174+
("generic class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False, False),
175+
("generic objc metatype", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False, False),
176+
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False, False),
171177
]
172178
}%
173179

174-
% for testName, valueExpr, testFunc, conformsToError in testCases:
180+
% for testName, valueExpr, testFunc, conformsToError, conformsToHashable in testCases:
175181
BridgeAnything.test("${testName}") {
176-
do {
182+
autoreleasepool {
177183
let x = ${valueExpr}
178184
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(x))
179185
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(x))
180186

187+
// Bridge an array containing x.
188+
let xInArray = [x]
189+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0])
190+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0])
191+
if (x as? NSObject) != nil {
192+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0])
193+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0])
194+
}
195+
196+
// Bridge a dictionary containing x as a value.
197+
let xInDictValue = ["key" : x]
198+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!)
199+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!)
200+
if (x as? NSObject) != nil {
201+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!)
202+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!)
203+
}
204+
205+
% if conformsToHashable:
206+
// Bridge a dictionary containing x as a key.
207+
let xInDictKey = [x : "value"] as [AnyHashable: String]
208+
// FIXME: need a way to express `AnyObject & Hashable`.
209+
// The NSObject version below can't test class LifetimeTracked.
210+
// ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!)
211+
// ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!)
212+
if (x as? NSObject) != nil {
213+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!)
214+
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!)
215+
}
216+
217+
% end
218+
181219
let xInAny: Any = x
182220
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(xInAny))
183221
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(xInAny))

0 commit comments

Comments
 (0)