Skip to content

[runtime] Fix some casts of _SwiftValue #8457

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 4 commits into from
Apr 10, 2017
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
16 changes: 12 additions & 4 deletions stdlib/private/StdlibUnittest/LifetimeTracked.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ public final class LifetimeTracked {
public var serialNumber: Int = 0
}

extension LifetimeTracked : Equatable {
public static func == (x: LifetimeTracked, y: LifetimeTracked) -> Bool {
return x.value == y.value
}
}

extension LifetimeTracked : Hashable {
public var hashValue: Int {
return value
}
}

extension LifetimeTracked : Strideable {
public func distance(to other: LifetimeTracked) -> Int {
return self.value.distance(to: other.value)
Expand All @@ -58,10 +70,6 @@ extension LifetimeTracked : CustomStringConvertible {
}
}

public func == (x: LifetimeTracked, y: LifetimeTracked) -> Bool {
return x.value == y.value
}

public func < (x: LifetimeTracked, y: LifetimeTracked) -> Bool {
return x.value < y.value
}
24 changes: 19 additions & 5 deletions stdlib/public/runtime/Casting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,10 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
const Metadata *srcType,
const Metadata *targetType,
DynamicCastFlags flags) {
// These flag combinations are not handled here.
assert(!(flags & DynamicCastFlags::Unconditional));
assert(!(flags & DynamicCastFlags::DestroyOnFailure));

// Swift type should be AnyObject or a class type.
if (!srcType->isAnyClass()) {
auto existential = dyn_cast<ExistentialTypeMetadata>(srcType);
Expand Down Expand Up @@ -2234,7 +2238,8 @@ static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,

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

#if SWIFT_OBJC_INTEROP
// A class or AnyObject reference may point at a boxed _SwiftValue.
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
targetType, flags)) {
return true;
// A class or AnyObject reference may point to a _SwiftValue box.
{
auto innerFlags = flags - DynamicCastFlags::Unconditional
- DynamicCastFlags::DestroyOnFailure;
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
targetType, innerFlags)) {
// TakeOnSuccess was handled inside tryDynamicCastBoxedSwiftValue().
return true;
} else {
// Couldn't cast boxed value to targetType.
// Fall through and try to cast the _SwiftValue box itself to targetType.
// (for example, casting _SwiftValue to NSObject will be successful)
}
}
#endif

Expand Down
98 changes: 68 additions & 30 deletions test/stdlib/BridgeIdAsAny.swift.gyb
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@ func wantonlyWrapInAny<T>(_ x: T) -> Any {
extension LifetimeTracked: Error {}
extension String: Error {}

struct KnownUnbridged: Equatable, Error {
struct KnownUnbridged: Equatable, Hashable, Error {
var x, y: LifetimeTracked

init() {
x = LifetimeTracked(17)
y = LifetimeTracked(38)
}
}
func ==(a: KnownUnbridged, b: KnownUnbridged) -> Bool {
return a.x === b.x && a.y === b.y

public static func ==(a: KnownUnbridged, b: KnownUnbridged) -> Bool {
return a.x === b.x && a.y === b.y
}

public var hashValue: Int {
return x.hashValue ^ y.hashValue
}
}

struct KnownUnbridgedWithDescription: CustomStringConvertible,
Expand Down Expand Up @@ -96,22 +101,22 @@ func boxedTypeRoundTripsThroughDynamicCasting(original: KnownUnbridged,

func tupleCanBeDynamicallyCast(original: (Int, String),
bridged: AnyObject) {
expectTrue(original == bridged as! (Int, String))
expectTrue(original == (bridged as! (Int, String)))
}
func metatypeCanBeDynamicallyCast(original: Int.Type,
bridged: AnyObject) {
expectTrue(original == bridged as! Int.Type)
expectTrue(original == bridged as! Any.Type)
expectTrue(original == (bridged as! Int.Type))
expectTrue(original == (bridged as! Any.Type))
}
func metatypeCanBeDynamicallyCast(original: CFString.Type,
bridged: AnyObject) {
expectTrue(original == bridged as! CFString.Type)
expectTrue(original == bridged as! Any.Type)
expectTrue(original == (bridged as! CFString.Type))
expectTrue(original == (bridged as! Any.Type))
}
func metatypeCanBeDynamicallyCastGenerically<T>(original: T.Type,
bridged: AnyObject) {
expectTrue(original == bridged as! T.Type)
expectTrue(original == bridged as! Any.Type)
expectTrue(original == (bridged as! T.Type))
expectTrue(original == (bridged as! Any.Type))
}


Expand Down Expand Up @@ -151,33 +156,66 @@ protocol P {}
// interesting bridging cases in different kinds of existential container.
%{
testCases = [
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True),
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True),
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True),
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False),
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False),
("generic metatype", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False),
("CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCast", False),
("generic CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False),
("class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False),
("objc metatype", 'NSObject.self', "classMetatypePreservesIdentity", False),
("protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False),
("objc protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False),
("objc protocol composition", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False),
("mixed protocol composition", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False),
("generic class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False),
("generic objc metatype", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False),
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False),
# testName valueExpr testFunc conformsToError conformsToHashable
("classes", "LifetimeTracked(0)", "bridgedObjectPreservesIdentity", True, True),
("strings", '"vitameatavegamin"', "stringBridgesToEqualNSString", True, True),
("unbridged type", "KnownUnbridged()", "boxedTypeRoundTripsThroughDynamicCasting", True, True),
("tuple", '(1, "2")', "tupleCanBeDynamicallyCast", False, False),
("metatype", 'Int.self', "metatypeCanBeDynamicallyCast", False, False),
("generic metatype", 'Int.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
("CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCast", False, False),
("generic CF metatype", 'CFString.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
("class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentity", False, False),
("objc metatype", 'NSObject.self', "classMetatypePreservesIdentity", False, False),
("protocol", 'P.self', "metatypeCanBeDynamicallyCastGenerically", False, False),
("objc protocol", 'NSCopying.self', "protocolObjectPreservesIdentity", False, False),
("objc protocol composition", '(NSCopying & NSCoding).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
("mixed protocol composition", '(NSCopying & P).self', "metatypeCanBeDynamicallyCastGenerically", False, False),
("generic class metatype", 'LifetimeTracked.self', "classMetatypePreservesIdentityGenerically", False, False),
("generic objc metatype", 'NSObject.self', "classMetatypePreservesIdentityGenerically", False, False),
("function", 'guineaPigFunction', "functionCanBeDynamicallyCast", False, False),
]
}%

% for testName, valueExpr, testFunc, conformsToError in testCases:
% for testName, valueExpr, testFunc, conformsToError, conformsToHashable in testCases:
BridgeAnything.test("${testName}") {
do {
autoreleasepool {
let x = ${valueExpr}
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(x))
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(x))

// Bridge an array containing x.
let xInArray = [x]
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0])
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0])
if (x as? NSObject) != nil {
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as! [AnyObject])[0])
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInArray) as? [AnyObject])![0])
}

// Bridge a dictionary containing x as a value.
let xInDictValue = ["key" : x]
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: AnyObject])["key"]!)
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: AnyObject])!["key"]!)
if (x as? NSObject) != nil {
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as! [String: NSObject])["key"]!)
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictValue) as? [String: NSObject])!["key"]!)
}

% if conformsToHashable:
// Bridge a dictionary containing x as a key.
let xInDictKey = [x : "value"] as [AnyHashable: String]
// FIXME: need a way to express `AnyObject & Hashable`.
// The NSObject version below can't test class LifetimeTracked.
// ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [(AnyObject & Hashable): String]).keys.first!)
// ${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [(AnyObject & Hashable): String])!.keys.first!)
if (x as? NSObject) != nil {
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as! [NSObject: String]).keys.first!)
${testFunc}(original: x, bridged: (_bridgeAnythingToObjectiveC(xInDictKey) as? [NSObject: String])!.keys.first!)
}

% end

let xInAny: Any = x
${testFunc}(original: x, bridged: _bridgeAnythingToObjectiveC(xInAny))
${testFunc}(original: x, bridged: _bridgeAnythingNonVerbatimToObjectiveC(xInAny))
Expand Down