mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
It would be great if this could also set the availability for the `code:` block, but this at least cuts out some boilerplate.
1094 lines
32 KiB
Swift
1094 lines
32 KiB
Swift
// Casts.swift - Tests for conversion between types.
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
// -----------------------------------------------------------------------------
|
|
///
|
|
/// Contains tests for non-trapping type conversions reported by users.
|
|
///
|
|
// -----------------------------------------------------------------------------
|
|
// RUN: %empty-directory(%t)
|
|
//
|
|
// RUN: %target-build-swift -swift-version 5 -g -Onone -module-name a %s -o %t/a.swift5.Onone.out
|
|
// RUN: %target-codesign %t/a.swift5.Onone.out
|
|
// RUN: %target-run %t/a.swift5.Onone.out
|
|
//
|
|
// RUN: %target-build-swift -swift-version 5 -g -O -module-name a %s -o %t/a.swift5.O.out
|
|
// RUN: %target-codesign %t/a.swift5.O.out
|
|
// RUN: %target-run %t/a.swift5.O.out
|
|
//
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: concurrency
|
|
// UNSUPPORTED: use_os_stdlib
|
|
// UNSUPPORTED: back_deployment_runtime
|
|
|
|
import StdlibUnittest
|
|
#if _runtime(_ObjC)
|
|
import Foundation
|
|
#endif
|
|
|
|
func blackhole<T>(_ t: T) { }
|
|
|
|
private func runtimeCast<T,U>(_ from: T, to: U.Type) -> U? {
|
|
return from as? U
|
|
}
|
|
|
|
let CastsTests = TestSuite("Casts")
|
|
|
|
// https://github.com/apple/swift/issues/43043
|
|
// Missing release for some types after failed conversion
|
|
CastsTests.test("No leak for failed tuple casts") {
|
|
let t: Any = (1, LifetimeTracked(0))
|
|
expectFalse(t is Any.Type)
|
|
}
|
|
|
|
protocol P {}
|
|
class ErrClass : Error { }
|
|
|
|
// https://github.com/apple/swift/issues/43009
|
|
CastsTests.test("No overrelease of existential boxes in failed casts") {
|
|
// We fail casts of an existential box repeatedly to ensure it does not get
|
|
// over-released.
|
|
func bar<T>(_ t: T) {
|
|
for _ in 0..<10 {
|
|
if case let a as P = t {
|
|
_ = a
|
|
}
|
|
}
|
|
}
|
|
|
|
let err: Error = ErrClass()
|
|
bar(err)
|
|
}
|
|
|
|
extension Int : P {}
|
|
|
|
/// https://github.com/apple/swift/issues/50204
|
|
/// Inconsistent optional casting behaviour with generics
|
|
///
|
|
/// Runtime failed to unwrap multiple levels of `Optional` when casting.
|
|
CastsTests.test("Multi-level optionals can be casted") {
|
|
func testSuccess<From, To>(_ x: From, from: From.Type, to: To.Type) {
|
|
expectNotNil(x as? To)
|
|
}
|
|
func testFailure<From, To>(_ x: From, from: From.Type, to: To.Type) {
|
|
expectNil(x as? To)
|
|
}
|
|
testSuccess(42, from: Int?.self, to: Int.self)
|
|
testSuccess(42, from: Int??.self, to: Int.self)
|
|
testSuccess(42, from: Int???.self, to: Int.self)
|
|
testSuccess(42, from: Int???.self, to: Int?.self)
|
|
testSuccess(42, from: Int???.self, to: Int??.self)
|
|
testSuccess(42, from: Int???.self, to: Int???.self)
|
|
testFailure(42, from: Int?.self, to: String.self)
|
|
testFailure(42, from: Int??.self, to: String.self)
|
|
testFailure(42, from: Int???.self, to: String.self)
|
|
}
|
|
|
|
/// https://github.com/apple/swift/issues/52251
|
|
/// `Optional<T>.none` not casting to `Optional<U>.none` in generic context
|
|
CastsTests.test("Optional<T>.none can be casted to Optional<U>.none in generic context") {
|
|
func test<T>(_ type: T.Type) -> T? {
|
|
return Any?.none as? T
|
|
}
|
|
|
|
expectEqual(type(of: test(Bool.self)), Bool?.self)
|
|
expectEqual(type(of: test(Bool?.self)), Bool??.self)
|
|
}
|
|
|
|
/// https://github.com/apple/swift/issues/46456
|
|
/// Failure to cast from ObjC existential without going through `AnyObject`
|
|
#if _runtime(_ObjC)
|
|
protocol P2 {}
|
|
CastsTests.test("Cast from ObjC existential to Protocol") {
|
|
if #available(SwiftStdlib 5.3, *) {
|
|
struct S: P2 {}
|
|
|
|
class ObjCWrapper {
|
|
@objc dynamic let any: Any = S()
|
|
init() {}
|
|
}
|
|
let a = ObjCWrapper().any
|
|
expectTrue(a is P2)
|
|
// The following cast failed in the above issue (everything else here
|
|
// succeeded).
|
|
expectNotNil(a as? P2)
|
|
expectNotNil(a as? S)
|
|
let b = a as AnyObject
|
|
expectTrue(a is P2)
|
|
expectNotNil(b as? P2)
|
|
expectNotNil(b as? S)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
protocol P3 {}
|
|
CastsTests.test("Cast from Swift existential to Protocol") {
|
|
struct S: P3 {}
|
|
class SwiftWrapper {
|
|
let any: Any = S()
|
|
init() {}
|
|
}
|
|
let a = SwiftWrapper().any
|
|
expectTrue(a is P3)
|
|
expectNotNil(a as? P3)
|
|
expectNotNil(a as? S)
|
|
let b = a as AnyObject
|
|
expectTrue(b is P3)
|
|
expectNotNil(b as? P3)
|
|
expectNotNil(b as? S)
|
|
}
|
|
|
|
/// Another test for https://github.com/apple/swift/issues/46456
|
|
/// User type in a `_SwiftValue` in an `Optional<Any>` not casting to a
|
|
/// protocol
|
|
///
|
|
/// Note: This uses the (misnamed) `_bridgeAnythingToObjectiveC` so it can
|
|
/// test these paths on Linux as well.
|
|
protocol P4 {}
|
|
CastsTests.test("struct -> Obj-C -> Protocol") {
|
|
struct SFUUUHREEEEEFFFF: P4 {
|
|
let value: Int
|
|
let tracker = LifetimeTracked(13)
|
|
}
|
|
|
|
let a: P4 = SFUUUHREEEEEFFFF(value: 13)
|
|
|
|
let b = _bridgeAnythingToObjectiveC(a)
|
|
let d = b as? Any
|
|
let e = d as? P4
|
|
expectNotNil(e)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
extension CFBitVector : P {
|
|
static func makeImmutable(from values: Array<UInt8>) -> CFBitVector {
|
|
return CFBitVectorCreate(/*allocator:*/ nil, values, values.count * 8)
|
|
}
|
|
}
|
|
|
|
extension CFMutableBitVector {
|
|
static func makeMutable(from values: Array<UInt8>) -> CFMutableBitVector {
|
|
return CFBitVectorCreateMutableCopy(
|
|
/*allocator:*/ nil,
|
|
/*capacity:*/ 0,
|
|
CFBitVector.makeImmutable(from: values))
|
|
}
|
|
}
|
|
|
|
func isP<T>(_ t: T) -> Bool {
|
|
return t is P
|
|
}
|
|
|
|
// https://github.com/apple/swift/issues/44896
|
|
CastsTests.test("Dynamic casts of CF types to protocol existentials")
|
|
.skip(.custom({
|
|
!_isDebugAssertConfiguration()
|
|
},
|
|
reason: "This test behaves unpredictably in optimized mode."))
|
|
.code {
|
|
expectTrue(isP(10 as Int))
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
expectTrue(isP(CFBitVector.makeImmutable(from: [10, 20])))
|
|
expectTrue(isP(CFMutableBitVector.makeMutable(from: [10, 20])))
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// https://github.com/apple/swift/issues/47129
|
|
protocol P_47129 {}
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
CastsTests.test("Any(Optional(T)) -> Protocol") {
|
|
struct S: P_47129 {
|
|
let tracker = LifetimeTracked(13)
|
|
}
|
|
|
|
let a = S()
|
|
let b: S? = a
|
|
let c = b as? Any
|
|
let d = c as? P_47129
|
|
expectNotNil(d)
|
|
}
|
|
}
|
|
|
|
// rdar://27108240 (Optional casting bug (crash))
|
|
protocol Key {
|
|
associatedtype Value
|
|
}
|
|
CastsTests.test("Cast to associated type") {
|
|
// Helper function to bypass compiler cast optimizations
|
|
func runtimeCast<From, To> (_ x: From, to: To.Type) -> To? {
|
|
return x as? To
|
|
}
|
|
struct StringKey : Key {
|
|
typealias Value = String?
|
|
}
|
|
var string: String?
|
|
func value<K: Key>(forKey key: K.Type) {
|
|
let b = runtimeCast(string, to: K.Value.self)
|
|
expectNotNil(b)
|
|
let c = string as? K.Value
|
|
expectNotNil(c)
|
|
}
|
|
value(forKey: StringKey.self)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// rdar://36288786 (Swift metatype stored in an Objective-C id property can't be typecast back to its original type)
|
|
CastsTests.test("Store Swift metatype in ObjC property and cast back to Any.Type") {
|
|
class MyObj {
|
|
var sVar: Any? = nil
|
|
@objc dynamic var objcVar: Any? = nil
|
|
}
|
|
|
|
let a = MyObj()
|
|
|
|
// Double values
|
|
a.sVar = 1.234
|
|
a.objcVar = 1.234
|
|
|
|
let sValue1 = a.sVar as? Double
|
|
let objcValue1 = a.objcVar as? Double
|
|
expectEqual(sValue1, objcValue1)
|
|
|
|
// Swift types
|
|
let b = Bool.self
|
|
a.sVar = b
|
|
a.objcVar = b
|
|
|
|
let sValue2 = a.sVar as? Any.Type
|
|
let objcValue2 = a.objcVar as? Any.Type
|
|
expectTrue(sValue2 == b)
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
expectTrue(sValue2 == objcValue2)
|
|
expectTrue(objcValue2 == b)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/// rdar://37793004
|
|
/// https://github.com/apple/swift/issues/49597
|
|
CastsTests.test("Cast enum back from AnyHashable") {
|
|
enum E {
|
|
case a
|
|
}
|
|
|
|
// This works as expected.
|
|
let str: AnyHashable = "hello"
|
|
expectNotNil(str as? String) // Optional("hello")
|
|
expectNotNil(str as? String as Any) // Optional("hello")
|
|
|
|
// This doesn't.
|
|
let ea: AnyHashable = E.a
|
|
expectNotNil(ea as? E)
|
|
expectNotNil(ea as? E as Any)
|
|
expectEqual((ea as? E), E.a)
|
|
}
|
|
|
|
/// rdar://39415812
|
|
/// https://github.com/apple/swift/issues/49975
|
|
/// Failure to see through boxed `_SwiftValue` when casting from `@objc` Type
|
|
#if _runtime(_ObjC)
|
|
@objc(Exporter)
|
|
protocol Exporter: NSObjectProtocol {
|
|
var type: Any { get }
|
|
func export(item: Any) -> String?
|
|
}
|
|
CastsTests.test("Casts from @objc Type") {
|
|
struct User { var name: String }
|
|
|
|
final class UserExporter: NSObject, Exporter {
|
|
var type: Any { return User.self }
|
|
func export(item: Any) -> String? {
|
|
let u = item as? User
|
|
return u?.name
|
|
}
|
|
}
|
|
|
|
let user = User(name: "Kermit")
|
|
let exporter: Exporter = UserExporter()
|
|
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
expectTrue(exporter.type is User.Type)
|
|
}
|
|
expectNotNil(exporter.export(item: user))
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
// rdar://44467533 (Swift main branch: conditional casts for _ObjectiveCBridgeable miscompile in swift-corelibs-foundation)
|
|
CastsTests.test("Conditional NSNumber -> Bool casts") {
|
|
let x = NSNumber(value: -1) as? Bool
|
|
expectNil(x)
|
|
}
|
|
#endif
|
|
|
|
/// rdar://45217461
|
|
/// https://github.com/apple/swift/issues/51469
|
|
/// Type check operator (`is`) fails for `Any!` variable holding an `Error`
|
|
/// (struct) value
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
CastsTests.test("Casts from Any(struct) to Error") {
|
|
struct MyError: Error { }
|
|
|
|
let a: Any! = MyError()
|
|
let b: Any = a
|
|
expectTrue(b is Error)
|
|
}
|
|
}
|
|
|
|
CastsTests.test("Cast failure for Any! holding Error struct") {
|
|
struct MyError: Error {}
|
|
let a: Any! = MyError()
|
|
let b: Any = a
|
|
expectTrue(b is Error)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// rdar://15494623 (Handle dynamic cast to archetype bound to ObjC existential)
|
|
CastsTests.test("Dynamic cast to ObjC protocol") {
|
|
func genericCast<T>(x: NSObject, _: T.Type) -> T? {
|
|
return x as? T
|
|
}
|
|
|
|
let n: NSNumber = 1
|
|
let copying = genericCast(x: n, NSCopying.self)
|
|
expectNotNil(copying)
|
|
}
|
|
#endif
|
|
|
|
// https://github.com/apple/swift/issues/48681
|
|
if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) {
|
|
CastsTests.test("Nil handling for Optionals and Arrays") {
|
|
func check(_ arg: Int??) -> String {
|
|
switch arg {
|
|
case .none:
|
|
return ".none"
|
|
case .some(.none):
|
|
return ".some(.none)"
|
|
case .some(.some):
|
|
return ".some(.some)"
|
|
}
|
|
}
|
|
|
|
let x: Int? = .none
|
|
let y: [Int?] = [.none]
|
|
|
|
let a = x as Int??
|
|
let b = (x as? Int??)!
|
|
let b2 = runtimeCast(x, to: Int??.self)!
|
|
let c = Int?.none as Int??
|
|
let d = (Int?.none as? Int??)!
|
|
let d2 = runtimeCast(Int?.none, to: Int??.self)!
|
|
let e = (y as [Int??]).first!
|
|
let f = (y as? [Int??])!.first!
|
|
let f2 = runtimeCast(y, to: [Int??].self)!.first!
|
|
let g = ([Int?.none] as [Int??]).first!
|
|
let h = ([Int?.none] as? [Int??])!.first!
|
|
let h2 = runtimeCast([Int?.none], to: [Int??].self)!.first!
|
|
|
|
// Original reporter believes all of these should be .some(.none)
|
|
expectEqual(".some(.none)", check(a)) // Xcode 9.0: .some(.none)
|
|
expectEqual(".some(.none)", check(b)) // Xcode 9.0: .some(.none)
|
|
expectEqual(".some(.none)", check(b2))
|
|
expectEqual(".some(.none)", check(c)) // Xcode 9.0: .some(.none)
|
|
expectEqual(".some(.none)", check(d)) // Xcode 9.0: .some(.none)
|
|
expectEqual(".some(.none)", check(d2))
|
|
expectEqual(".some(.none)", check(e)) // Xcode 9.0: .none
|
|
expectEqual(".some(.none)", check(f)) // Xcode 9.0: .none
|
|
expectEqual(".some(.none)", check(f2))
|
|
expectEqual(".some(.none)", check(g)) // Xcode 9.0: .some(.none)
|
|
expectEqual(".some(.none)", check(h)) // Xcode 9.0: .none
|
|
expectEqual(".some(.none)", check(h2))
|
|
}
|
|
}
|
|
|
|
protocol SwiftProtocol {}
|
|
CastsTests.test("Swift Protocol Metatypes don't self-conform") {
|
|
let a = SwiftProtocol.self
|
|
// `is P.Protocol` tests whether the argument is a subtype of P.
|
|
// In particular, the protocol identifier `P.self` is such a subtype.
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
expectNotNil(runtimeCast(a, to: SwiftProtocol.Protocol.self)) // Fixed by rdar://58991956
|
|
}
|
|
expectNotNil(a as? SwiftProtocol.Protocol)
|
|
expectTrue(a is SwiftProtocol.Protocol)
|
|
blackhole(a as! SwiftProtocol.Protocol) // Should not trap
|
|
|
|
// `is P.Type` tests conformance to P. Protocols cannot conform to
|
|
// protocols, so these always fail.
|
|
expectNil(runtimeCast(a, to: SwiftProtocol.Type.self))
|
|
expectNil(a as? SwiftProtocol.Type)
|
|
expectFalse(a is SwiftProtocol.Type)
|
|
}
|
|
|
|
CastsTests.test("Self-conformance for Any.self") {
|
|
let b = Any.self
|
|
expectNotNil(runtimeCast(b, to: Any.Protocol.self))
|
|
blackhole(b as! Any.Protocol) // Should not trap
|
|
expectTrue(b is Any.Protocol)
|
|
expectNotNil(b as? Any.Protocol)
|
|
|
|
// Unlike most other protocols, Any.self does conform to Any
|
|
expectNotNil(runtimeCast(b, to: Any.Type.self))
|
|
expectNotNil(b as? Any.Type)
|
|
expectTrue(b is Any.Type)
|
|
blackhole(b as! Any.Type)
|
|
}
|
|
|
|
// rdar://59067748 (Error Protocol should self-conform in optimized casts)
|
|
CastsTests.test("Self-conformance for Error.self")
|
|
.skip(.custom({
|
|
!_isDebugAssertConfiguration()
|
|
},
|
|
reason: "Cast optimizer breaks this test"))
|
|
.code {
|
|
let c = Error.self
|
|
expectNotNil(runtimeCast(c, to: Error.Protocol.self))
|
|
expectNotNil(c as? Error.Protocol)
|
|
expectTrue(c is Error.Protocol)
|
|
blackhole(c as! Error.Protocol)
|
|
|
|
// Unlike most other protocols, Error.self does conform to Error
|
|
expectNotNil(runtimeCast(c, to: Error.Type.self))
|
|
expectFailure { expectNotNil(c as? Error.Type) }
|
|
expectFailure { expectTrue(c is Error.Type) }
|
|
// blackhole(c as! Error.Type) // Should not trap, but currently does
|
|
}
|
|
|
|
// rdar://59067556 (Obj-C Protocol Metatypes should self-conform)
|
|
#if _runtime(_ObjC)
|
|
@objc protocol ObjCProtocol {}
|
|
CastsTests.test("ObjC Protocol Metatypes self-conform") {
|
|
let a = ObjCProtocol.self
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
expectNotNil(runtimeCast(a, to: ObjCProtocol.Protocol.self))
|
|
}
|
|
expectNotNil(a as? ObjCProtocol.Protocol)
|
|
expectTrue(a is ObjCProtocol.Protocol)
|
|
blackhole(a as! ObjCProtocol.Protocol)
|
|
|
|
// Unlike Swift protocols, ObjC protocols do conform to themselves
|
|
expectFailure { expectNotNil(runtimeCast(a, to: ObjCProtocol.Type.self)) }
|
|
expectFailure { expectNotNil(a as? ObjCProtocol.Type) }
|
|
expectFailure { expectTrue(a is ObjCProtocol.Type) }
|
|
// blackhole(a as! ObjCProtocol.Type) // Should not trap, but currently does
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
protocol NewStringProtocol {}
|
|
extension String: NewStringProtocol { }
|
|
CastsTests.test("String/NSString extension compat") {
|
|
let x: Any = NSString()
|
|
expectFailure { expectNotNil(runtimeCast(x, to: NewStringProtocol.self)) }
|
|
expectFailure { expectNotNil(x as? NewStringProtocol) }
|
|
}
|
|
#endif
|
|
|
|
// https://github.com/apple/swift/issues/44608
|
|
protocol P_44608 {}
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
CastsTests.test("Cast Any(Optional(class)) to Protocol type") {
|
|
class Foo: P_44608 { }
|
|
|
|
let optionalFoo : Foo? = Foo()
|
|
let anyValue: Any = optionalFoo
|
|
|
|
let foo1 = anyValue as? Foo
|
|
expectNotNil(foo1)
|
|
|
|
let foo2 = anyValue as? P_44608
|
|
expectNotNil(foo2)
|
|
|
|
let foo3 = runtimeCast(anyValue, to: Foo.self)
|
|
expectNotNil(foo3)
|
|
|
|
let foo4 = runtimeCast(anyValue, to: P_44608.self)
|
|
expectNotNil(foo4)
|
|
}
|
|
}
|
|
|
|
CastsTests.test("Cast from Any? to Existential") {
|
|
let a = Float(1) as Any as? Float
|
|
expectNotNil(a)
|
|
|
|
let b = Float(1) as Any as? CustomStringConvertible
|
|
expectNotNil(b)
|
|
|
|
let c = Optional.some(Float(1)) as Any as? Float
|
|
expectNotNil(c)
|
|
|
|
let d = Optional.some(Float(1)) as Any as? CustomStringConvertible
|
|
expectNotNil(d)
|
|
}
|
|
|
|
// https://github.com/apple/swift/issues/45505
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Dict value casting") {
|
|
var dict: [AnyHashable: String] = [:]
|
|
dict["Key"] = "Value"
|
|
expectNotNil(dict["Key"] as? NSString)
|
|
expectNotNil(runtimeCast(dict["Key"], to: NSString.self))
|
|
}
|
|
#endif
|
|
|
|
// https://github.com/apple/swift-corelibs-foundation/issues/3279
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("String coercions should work on Linux") {
|
|
let a = "abc" as Substring as NSString
|
|
let b = "abc" as NSString
|
|
expectEqual(a, b)
|
|
|
|
let c = "abc" as Substring
|
|
let d = c as? NSString
|
|
let e = "abc" as? NSString
|
|
expectEqual(d, e)
|
|
|
|
let f = runtimeCast(d, to: NSString.self)
|
|
expectEqual(e, f)
|
|
}
|
|
#endif
|
|
|
|
class ClassInt: Equatable, Hashable {
|
|
private var tracker = LifetimeTracked(77)
|
|
static func == (lhs: ClassInt, rhs: ClassInt) -> Bool {return true}
|
|
func hash(into hasher: inout Hasher) {}
|
|
}
|
|
CastsTests.test("AnyHashable(Class) -> Obj-C -> Class") {
|
|
let a = ClassInt()
|
|
let b = runtimeCast(a, to: AnyHashable.self)!
|
|
let c = _bridgeAnythingToObjectiveC(b)
|
|
let d = /* SwiftValueBox(AnyHashable(ClassInt)) */ c as? ClassInt
|
|
expectNotNil(d)
|
|
let d2 = runtimeCast(c, to: ClassInt.self)
|
|
expectNotNil(d2)
|
|
let e = runtimeCast(/* SwiftValueBox(AnyHashable(ClassInt)) */ c, to: ClassInt.self)
|
|
expectNotNil(e)
|
|
}
|
|
|
|
// rdar://58999120
|
|
// https://github.com/apple/swift-corelibs-foundation/issues/3274
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Error -> NSError -> Protocol transitivity") {
|
|
enum NonConformingError: Error {
|
|
case ok
|
|
}
|
|
|
|
let nonConformingError: Error = NonConformingError.ok
|
|
|
|
// NSError conforms to CustomStringConvertible, so ...
|
|
let conformingError = nonConformingError as? NSError
|
|
expectTrue(conformingError is CustomStringConvertible)
|
|
expectNotNil(conformingError as? CustomStringConvertible)
|
|
|
|
// Our error type does not conform directly, but should conform
|
|
// indirectly because of NSError...
|
|
// Note: Known broken in both runtime and compiler.
|
|
expectFailure { expectTrue(nonConformingError is CustomStringConvertible) }
|
|
expectFailure { expectNotNil(nonConformingError as? CustomStringConvertible) }
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Runtime crash casting Obj-C object to Obj-C protocol (rdar://16449805)") {
|
|
// FIXME: The reported crash was for `NSPoint(x:0, y:0) as? NSCoding`,
|
|
// but NSPoint seems to not be available on 32-bit platforms.
|
|
expectNotNil(NSString() as? NSCoding)
|
|
}
|
|
#endif
|
|
|
|
CastsTests.test("Casting Swift Error-conforming types to Error existentials") {
|
|
enum Foo: Error {
|
|
case OK
|
|
case Broken
|
|
}
|
|
let a = Foo.Broken
|
|
let b = a as? Error
|
|
expectNotNil(b)
|
|
let c = b as? Foo
|
|
expectNotNil(c)
|
|
let d = Foo.self as? Error.Type
|
|
expectNotNil(d)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Casting NSError <-> Error") {
|
|
@objc class Bar: NSError {
|
|
init() {super.init(domain: "Bar", code: 99)}
|
|
required init?(coder: NSCoder) {super.init(coder: coder)}
|
|
}
|
|
let e = Bar.self as? Error.Type
|
|
expectNotNil(e)
|
|
let f = Bar.self as? Bar.Type
|
|
expectNotNil(f)
|
|
let g = Bar() as? Error
|
|
expectNotNil(g)
|
|
}
|
|
#endif
|
|
|
|
// Foundation's JSON handling makes heavy use of passing Any? inside of Any
|
|
// existentials. That inspired the following three checks:
|
|
CastsTests.test("[Any(Any?)] -> [Any?] should prefer unwrapping source") {
|
|
let a: Any? = nil
|
|
let b: [Any] = [a as Any]
|
|
let c = b as? [Any?]
|
|
let d = c!
|
|
let e = d[0]
|
|
expectNil(e)
|
|
}
|
|
|
|
CastsTests.test("Any(Any?) -> Any? should prefer unwrapping source") {
|
|
let a: Any? = nil
|
|
let b: Any = a
|
|
let c = b as? Any?
|
|
let d = c!
|
|
expectNil(d)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("NSNull?.none -> Any? should set outer nil") {
|
|
let a: NSNull? = nil
|
|
let b = a as? Any?
|
|
let c = b!
|
|
expectNil(c)
|
|
}
|
|
#endif
|
|
|
|
if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) {
|
|
CastsTests.test("Int??.some(nil) => Int??? should inject naturally") {
|
|
let a: Int?? = .some(nil)
|
|
let b = a as? Int???
|
|
let c = b!
|
|
let d = c!
|
|
let e = d!
|
|
expectNil(e)
|
|
}
|
|
}
|
|
|
|
if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) {
|
|
CastsTests.test("Int??.some(nil) => String??? should inject naturally") {
|
|
let a: Int?? = .some(nil)
|
|
let b = runtimeCast(a, to: String???.self)
|
|
let c = b!
|
|
let d = c!
|
|
let e = d!
|
|
expectNil(e)
|
|
}
|
|
}
|
|
|
|
if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) {
|
|
CastsTests.test("Int??.some(nil) => Any??? should inject naturally") {
|
|
let a: Int?? = .some(nil)
|
|
let b = a as? Any???
|
|
let c = b!
|
|
let d = c!
|
|
let e = d!
|
|
expectNil(e)
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("NSString -> String fast path") {
|
|
let a = "short" as NSString
|
|
expectNotNil(a as? String)
|
|
let b = runtimeCast(a, to: String.self)
|
|
expectNotNil(b)
|
|
|
|
let c = "Long (adj) -- extended, large, the opposite of short" as NSString
|
|
expectNotNil(c as? String)
|
|
let d = runtimeCast(c, to: String.self)
|
|
expectNotNil(d)
|
|
|
|
let e = NSMutableString("not read-only")
|
|
expectNotNil(e as? String)
|
|
let f = runtimeCast(e, to: String.self)
|
|
expectNotNil(f)
|
|
|
|
let g = CFStringCreateWithCString(nil, "hello, world", CFStringBuiltInEncodings.UTF8.rawValue)
|
|
expectNotNil(g as? String)
|
|
let h = runtimeCast(g, to: String.self)
|
|
expectNotNil(h)
|
|
|
|
let i = CFStringCreateMutable(nil, 0)
|
|
expectNotNil(i as? String)
|
|
let j = runtimeCast(i, to: String.self)
|
|
expectNotNil(j)
|
|
}
|
|
#endif
|
|
|
|
// This fails in optimized builds because after inlining `runtimeCast`,
|
|
// the resulting SIL cast operation is left in a form that IRGen can't
|
|
// correctly handle.
|
|
//CastsTests.test("Optimized metatype -> AnyObject cast") {
|
|
// struct StructInt { }
|
|
// let a = StructInt.self
|
|
// let b = runtimeCast(a, to: AnyObject.self)
|
|
// expectNotNil(b)
|
|
//}
|
|
|
|
CastsTests.test("Any.Protocol") {
|
|
class C {}
|
|
struct S {}
|
|
func isAnyProtocol<T>(_ type: T.Type) -> Bool {
|
|
let result = T.self is Any.Protocol
|
|
if result {
|
|
// `as!` should succeed if `is` does
|
|
blackhole(T.self as! Any.Protocol)
|
|
}
|
|
return result
|
|
}
|
|
func isAnyType<T>(_ type: T.Type) -> Bool {
|
|
return T.self is Any.Type
|
|
}
|
|
func isType<T,U>(_ type: T.Type, to: U.Type) -> Bool {
|
|
return T.self is U.Type
|
|
}
|
|
|
|
expectTrue(Int.self is Any.Type)
|
|
expectNotNil(Int.self as? Any.Type)
|
|
expectTrue(isAnyType(Int.self))
|
|
expectFalse(Int.self is Any.Protocol)
|
|
expectNil(Int.self as? Any.Protocol)
|
|
expectFalse(isAnyProtocol(Int.self))
|
|
expectFalse(isType(Int.self, to: Any.self))
|
|
|
|
expectTrue(C.self is Any.Type)
|
|
expectNotNil(C.self as? Any.Type)
|
|
expectTrue(isAnyType(C.self))
|
|
expectFalse(C.self is Any.Protocol)
|
|
expectNil(C.self as? Any.Protocol)
|
|
expectFalse(isAnyProtocol(C.self))
|
|
expectFalse(isType(C.self, to: Any.self))
|
|
|
|
expectTrue(S.self is Any.Type)
|
|
expectNotNil(S.self as? Any.Type)
|
|
expectTrue(isAnyType(S.self))
|
|
expectFalse(S.self is Any.Protocol)
|
|
expectNil(S.self as? Any.Protocol)
|
|
expectFalse(isAnyProtocol(S.self))
|
|
expectFalse(isType(S.self, to: Any.self))
|
|
|
|
expectTrue(Any.self is Any.Type)
|
|
expectNotNil(Any.self as? Any.Type)
|
|
expectTrue(isAnyType(Any.self))
|
|
expectTrue(Any.self is Any.Protocol)
|
|
expectNotNil(Any.self as? Any.Protocol)
|
|
expectTrue(isAnyProtocol(Any.self))
|
|
expectTrue(isType(Any.self, to: Any.self))
|
|
|
|
expectTrue(Any?.self is Any.Type)
|
|
expectNotNil(Any?.self as? Any.Type)
|
|
expectTrue(isAnyType(Any?.self))
|
|
expectFalse(Any?.self is Any.Protocol)
|
|
expectNil(Any?.self as? Any.Protocol)
|
|
expectFalse(isAnyProtocol(Any?.self))
|
|
expectFalse(isType(Any?.self, to: Any.self))
|
|
}
|
|
|
|
CastsTests.test("Async function types") {
|
|
let asyncFnType: Any.Type = (() async -> Void).self
|
|
let fnType: Any.Type = (() -> Void).self
|
|
|
|
expectTrue(fnType is (() -> Void).Type)
|
|
expectTrue(asyncFnType is (() async -> Void).Type)
|
|
expectFalse(fnType is (() async -> Void).Type)
|
|
expectFalse(asyncFnType is (() -> Void).Type)
|
|
}
|
|
|
|
// `Optional<Int>` is Hashable, so it must cast to AnyHashable,
|
|
// even if it contains a nil. (This was broken in 5.3 and earlier,
|
|
// but was fixed by the new dynamic cast runtime.)
|
|
CastsTests.test("Optional nil -> AnyHashable") {
|
|
let a : Int? = nil
|
|
expectNotNil(a as? AnyHashable)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// See below for notes about missing Linux functionality
|
|
// that prevents us from running this test there.
|
|
CastsTests.test("AnyObject.Type -> AnyObject") {
|
|
class C {}
|
|
let a = C.self
|
|
let b = a as? AnyObject.Type
|
|
expectNotNil(b)
|
|
// Note: On macOS, the following cast generates a call to
|
|
// `swift_dynamicCastMetatypeToObjectConditional` That function is currently
|
|
// unimplemented on Linux, so this cast always fails on Linux.
|
|
let c = b as? AnyObject
|
|
expectNotNil(c)
|
|
// Note: The following cast currently succeeds on Linux only by stuffing the
|
|
// source into a `__SwiftValue` container, which breaks the checks below.
|
|
let d = runtimeCast(b, to: AnyObject.self)
|
|
expectNotNil(d)
|
|
let e = c as? C.Type
|
|
expectNotNil(e)
|
|
let f = runtimeCast(d, to: C.Type.self)
|
|
expectNotNil(f)
|
|
// Verify that the round-trip casts yield exactly the same pointer. In
|
|
// particular, none of the casts above should fall back on stuffing the source
|
|
// into a `__SwiftValue` container.
|
|
expectTrue(c! === a)
|
|
expectTrue(d! === a)
|
|
expectTrue(e! === a)
|
|
expectTrue(f! === a)
|
|
}
|
|
#endif
|
|
|
|
// https://github.com/apple/swift/issues/56209
|
|
protocol Fruit {}
|
|
CastsTests.test("Generic type validation") {
|
|
func check<A, B>(a: A.Type, b: B.Type) -> Bool {
|
|
return (a is B.Type)
|
|
}
|
|
struct Apple: Fruit {}
|
|
expectFalse(check(a: Apple.self, b: Fruit.self))
|
|
expectFalse(Apple.self is Fruit.Protocol)
|
|
expectTrue(Apple.self is Fruit.Type)
|
|
}
|
|
|
|
// https://github.com/apple/swift/issues/48829
|
|
protocol A {}
|
|
CastsTests.test("Cast from Any to Optional<Protocol>") {
|
|
struct B: A {}
|
|
|
|
// If we have an optional instance, stored as an `Any`
|
|
let b: A? = B()
|
|
let c = b as Any
|
|
|
|
// This fails to cast, should succeed.
|
|
let d = c as? A
|
|
expectNotNil(d)
|
|
|
|
// There is a workaround, but not ideal.
|
|
func cast<T, U>(_ t: T, to: U.Type) -> U? {
|
|
return t as? U
|
|
}
|
|
let f = cast(c, to: Any?.self) as? A
|
|
expectNotNil(f)
|
|
}
|
|
|
|
protocol SuperProtocol{}
|
|
CastsTests.test("Casting Objects retained from KeyPaths to Protocols is not working properly") {
|
|
// This is the simplified reproduction from rdar://59844232 which doesn't
|
|
// actually use KeyPaths
|
|
class SubClass : SuperProtocol{}
|
|
let value = SubClass() as Any? as Any
|
|
|
|
expectNotNil(value as? SubClass)
|
|
expectNotNil(value as? SuperProtocol)
|
|
}
|
|
|
|
// https://github.com/apple/swift/issues/54462
|
|
// FIXME: Known to still be broken, but we can document the issue here.
|
|
#if _runtime(_ObjC)
|
|
public protocol SomeProtocol {}
|
|
extension NSString: SomeProtocol {}
|
|
CastsTests.test("NSDictionary -> Dictionary casting") {
|
|
// Create NSDictionary with one entry
|
|
var a = NSMutableDictionary()
|
|
a[NSString("key")] = NSString("value")
|
|
|
|
let v = NSString("value")
|
|
let v2 = v as? SomeProtocol
|
|
expectNotNil(v2)
|
|
|
|
// Test casting of the dictionary
|
|
let b = a as? [String:SomeProtocol]
|
|
expectFailure { expectNotNil(b) } // Expect non-nil, but see nil
|
|
let c = a as? [String:Any]
|
|
expectNotNil(c) // Non-nil (as expected)
|
|
let d = c as? [String:SomeProtocol]
|
|
expectNotNil(d) // Non-nil (as expected)
|
|
}
|
|
#endif
|
|
|
|
// Casting optionals to AnyHashable is a little peculiar
|
|
// TODO: It would be nice if AnyHashable(Optional("Foo")) == AnyHashable("Foo")
|
|
// (including as dictionary keys). That would make this a lot less confusing.
|
|
CastsTests.test("Optional cast to AnyHashable") {
|
|
let d: [String?: String] = ["FooKey": "FooValue", nil: "NilValue"]
|
|
// In Swift 5.3, this cast DOES unwrap the non-nil key
|
|
// We've deliberately tried to preserve that behavior in Swift 5.4
|
|
let d2 = d as [AnyHashable: String]
|
|
|
|
// After https://github.com/apple/swift/issues/51550 all four of the following
|
|
// should work:
|
|
let d3 = d2["FooKey" as String? as AnyHashable]
|
|
expectNil(d3)
|
|
let d4 = d2["FooKey" as String?]
|
|
expectNil(d4)
|
|
let d5 = d2["FooKey"]
|
|
expectNotNil(d5)
|
|
let d6 = d2["FooKey" as AnyHashable]
|
|
expectNotNil(d6)
|
|
|
|
// The nil key should be preserved and still function
|
|
let d7 = d2[String?.none as AnyHashable]
|
|
expectNotNil(d7)
|
|
|
|
// Direct casts via the runtime unwrap the optional
|
|
let a: String = "Foo"
|
|
let ah: AnyHashable = a
|
|
let b: String? = a
|
|
let bh = runtimeCast(b, to: AnyHashable.self)
|
|
expectEqual(bh, ah)
|
|
|
|
// Direct casts that don't go through the runtime don't unwrap the optional
|
|
// This is inconsistent with the runtime cast behavior above. We should
|
|
// probably change the runtime behavior above to work the same as this,
|
|
// but that should wait until https://github.com/apple/swift/issues/51550
|
|
// lands.
|
|
let x: String = "Baz"
|
|
let xh = x as AnyHashable
|
|
let y: String? = x
|
|
let yh = y as AnyHashable // Doesn't unwrap the optional
|
|
// xh is AnyHashable("Baz")
|
|
// yh is AnyHashable(Optional("Baz"))
|
|
expectNotEqual(xh, yh)
|
|
}
|
|
|
|
// Repeatedly casting to AnyHashable should still test equal.
|
|
// (This was broken for a while because repeatedly casting to
|
|
// AnyHashable could end up with multiple nested AnyHashables.)
|
|
// rdar://75180619
|
|
CastsTests.test("Recursive AnyHashable") {
|
|
struct P: Hashable {
|
|
var x: Int
|
|
}
|
|
struct S {
|
|
var x: AnyHashable?
|
|
init<T: Hashable>(_ x: T?) {
|
|
self.x = x
|
|
}
|
|
}
|
|
let p = P(x: 0)
|
|
let hp = p as AnyHashable?
|
|
print(hp.debugDescription)
|
|
let s = S(hp)
|
|
print(s.x.debugDescription)
|
|
expectEqual(s.x, hp)
|
|
expectEqual(s.x, p)
|
|
}
|
|
|
|
// rdar://78224322
|
|
// https://github.com/apple/swift/issues/56987
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Do not overuse __SwiftValue")
|
|
.require(.stdlib_5_9)
|
|
.code {
|
|
struct Bar {}
|
|
// This used to succeed because of overeager __SwiftValue
|
|
// boxing (and __SwiftValue does satisfy NSCopying)
|
|
expectFalse(Bar() is NSCopying)
|
|
expectNil(runtimeCast(Bar(), to: NSCopying.self))
|
|
expectFalse(Bar() as Any is NSCopying)
|
|
expectNil(runtimeCast(Bar() as Any, to: NSCopying.self))
|
|
|
|
// `Bar() as! AnyObject` gets boxed as a __SwiftValue.
|
|
// __SwiftValue does conform to NSCopying, but that should
|
|
// not be visible here.
|
|
let anyBar = Bar() as! AnyObject
|
|
expectNil(runtimeCast(anyBar, to: NSCopying.self))
|
|
expectFalse(anyBar is NSCopying)
|
|
|
|
class Foo {}
|
|
// Foo does not conform to NSCopying
|
|
// (This used to succeed due to over-eager __SwiftValue boxing)
|
|
expectFalse(Foo() is NSCopying)
|
|
expectNil(runtimeCast(Foo(), to: NSCopying.self))
|
|
expectFalse(Foo() as Any is NSCopying)
|
|
expectNil(runtimeCast(Foo() as Any, to: NSCopying.self))
|
|
|
|
// A type that really does conform should cast to NSCopying
|
|
class Foo2: NSCopying {
|
|
func copy(with: NSZone?) -> Any { return self }
|
|
}
|
|
expectTrue(Foo2() is NSCopying)
|
|
expectNotNil(runtimeCast(Foo2(), to: NSCopying.self))
|
|
expectTrue(Foo2() is AnyObject)
|
|
expectNotNil(runtimeCast(Foo2(), to: AnyObject.self))
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Artificial subclass protocol conformance") {
|
|
class SwiftClass: NSObject {}
|
|
let subclass: AnyClass = objc_allocateClassPair(SwiftClass.self,
|
|
"ArtificialSwiftSubclass", 0)!
|
|
objc_registerClassPair(subclass)
|
|
expectFalse(subclass is P.Type)
|
|
}
|
|
#endif
|
|
|
|
CastsTests.test("Do not overuse __SwiftValue (non-ObjC)") {
|
|
struct Bar {}
|
|
// This should succeed because this is what __SwiftValue boxing is for
|
|
expectTrue(Bar() is AnyObject)
|
|
expectTrue(Bar() as Any is AnyObject)
|
|
let a = Bar() as Any as! AnyObject
|
|
expectTrue(a is Bar)
|
|
|
|
class Foo {}
|
|
// Any class type can be cast to AnyObject
|
|
expectTrue(Foo() is AnyObject)
|
|
let b = Foo() as! AnyObject
|
|
expectTrue(b is Foo)
|
|
|
|
// As above, but force use of runtime casting
|
|
expectNotNil(runtimeCast(Bar(), to: AnyObject.self))
|
|
expectNotNil(runtimeCast(Bar() as Any, to: AnyObject.self))
|
|
expectNotNil(runtimeCast(a, to: Bar.self))
|
|
expectNotNil(runtimeCast(Foo(), to: AnyObject.self))
|
|
expectNotNil(runtimeCast(b, to: Foo.self))
|
|
}
|
|
|
|
CastsTests.test("Don't put AnyHashable inside AnyObject") {
|
|
class C: Hashable {
|
|
func hash(into hasher: inout Hasher) {}
|
|
static func ==(lhs: C, rhs: C) -> Bool { true }
|
|
}
|
|
let a = C()
|
|
let b = AnyHashable(a)
|
|
let c = a as! AnyObject
|
|
expectTrue(a === c)
|
|
let d = c as! C
|
|
expectTrue(a === d)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// We currently (as of Jan 2024) bridge NSSet to Swift with `x as!
|
|
// Set<NSObject>`, which in turn demands that __SwiftValue successfully cast to
|
|
// NSObject.
|
|
// So this nonsensical behavior can probably never be fixed.
|
|
// (It's nonsense because it implies that every Swift object is derived
|
|
// from NSObject.) See PR #68952 for an early attempt to change it which
|
|
// had to be reverted.
|
|
CastsTests.test("__SwiftValue should not be obvious to `is`")
|
|
.xfail(.always("Probably can never be fixed"))
|
|
.code {
|
|
struct S {}
|
|
let s = S() as AnyObject
|
|
expectFalse(s is NSObject)
|
|
}
|
|
#endif
|
|
|
|
// See above for reasons why this might need to remain broken forever,
|
|
// though I do have some hope for it.
|
|
CastsTests.test("type(of:) should look through __SwiftValue")
|
|
.xfail(.always("Known to be broken"))
|
|
.code {
|
|
struct S {}
|
|
let s = S() as AnyObject
|
|
let t = "\(type(of: s))"
|
|
expectEqual(t, "S") // Fails: currently says `__SwiftValue`
|
|
}
|
|
|
|
runAllTests()
|