mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* Refactor Bincompat Organize everything around internal functions that test for a particular OS version. Correctly handle cases where we don't know the version of the app. Make all bincompat functions consistently return `true` for the legacy semantics, `false` for new semantics. Consistently name them all to reflect this. * Conditionalize the support for SR-14635 SR-14635 pointed out a hole in the updated dynamic casting logic that allowed certain casts that should have been illegal. In particular, when casting certain types to Obj-C protocols, the Swift value gets boxed; we would permit the cast to succeed whenever the resulting box satisfied the protocol. For example, this allowed any Swift value to be cast to `NSCopying` regardless of whether or not it implemented the required `copy(with:)` method. This was fixed in #37683 to reject such casts but of course some folks were depending on this behavior to pass Swift data into Obj-C functions. (The properly supported approach for passing arbitrary Swift data into Obj-C functions is to cast the Swift value to `AnyObject`.) This change makes that new behavior conditional. For now, the legacy semantics are enabled on Apple platforms and the new semantics are in use everywhere else. This will allow us to gradually enable enforcement of the new behavior over time. * Just skip this test on Apple platforms, since it is inconsistently implemented there (and is therefore not really testable)
1016 lines
30 KiB
Swift
1016 lines
30 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")
|
|
|
|
// Test for SR-426: 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 { }
|
|
|
|
CastsTests.test("No overrelease of existential boxes in failed casts") {
|
|
// Test for crash from SR-392
|
|
// 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 {}
|
|
|
|
// Test for SR-7664: 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)
|
|
}
|
|
|
|
// Test for SR-9837: 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)
|
|
}
|
|
|
|
// Test for SR-3871: Cannot cast from ObjC existential without going through AnyObject
|
|
#if _runtime(_ObjC)
|
|
protocol P2 {}
|
|
CastsTests.test("Cast from ObjC existential to Protocol (SR-3871)") {
|
|
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)
|
|
// In SR-3871, the following cast failed (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)
|
|
}
|
|
|
|
|
|
#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
|
|
}
|
|
|
|
CastsTests.test("Dynamic casts of CF types to protocol existentials (SR-2289)")
|
|
.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
|
|
|
|
// Another test for SR-3871, SR-5590, SR-6309, SR-8651:
|
|
// user type in a _SwiftValue in an Optional<Any> can't be cast to a protocol.
|
|
// Note: This uses the (misnamed) _bridgeAnythingToObjectiveC so it can
|
|
// test these paths on Linux as well.
|
|
protocol P6309 {}
|
|
CastsTests.test("Casting struct -> Obj-C -> Protocol fails (SR-3871, SR-5590, SR-6309, SR-8651)") {
|
|
struct S: P6309 {
|
|
let value: Int
|
|
let tracker = LifetimeTracked(13)
|
|
}
|
|
|
|
let a: P6309 = S(value: 13)
|
|
|
|
let b = _bridgeAnythingToObjectiveC(a)
|
|
let d = b as? Any
|
|
let e = d as? P6309
|
|
expectNotNil(e)
|
|
}
|
|
|
|
|
|
protocol P4552 {}
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
CastsTests.test("Casting Any(Optional(T)) -> Protocol fails (SR-4552)") {
|
|
struct S: P4552 {
|
|
let tracker = LifetimeTracked(13)
|
|
}
|
|
|
|
let a = S()
|
|
let b: S? = a
|
|
let c = b as? Any
|
|
let d = c as? P4552
|
|
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 ([dynamic casting] [SR-7049]: Enums don't cast back from AnyHashable)
|
|
CastsTests.test("Enums don't cast back from AnyHashable (SR-7049)") {
|
|
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)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
//rdar://39415812 ([dynamic casting] [SR-7432]: Can't see through boxed _SwiftValue when casting from @objc Type)
|
|
@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 ([dynamic casting] [SR-8964]: 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 (SR-8964)") {
|
|
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
|
|
|
|
// SR-6126
|
|
if #available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *) {
|
|
CastsTests.test("Nil handling for Optionals and Arrays (SR-6126)") {
|
|
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
|
|
|
|
protocol P1999 {}
|
|
if #available(SwiftStdlib 5.5, *) {
|
|
CastsTests.test("Cast Any(Optional(class)) to Protocol type (SR-1999)") {
|
|
class Foo: P1999 { }
|
|
|
|
let optionalFoo : Foo? = Foo()
|
|
let anyValue: Any = optionalFoo
|
|
|
|
let foo1 = anyValue as? Foo
|
|
expectNotNil(foo1)
|
|
|
|
let foo2 = anyValue as? P1999
|
|
expectNotNil(foo2)
|
|
|
|
let foo3 = runtimeCast(anyValue, to: Foo.self)
|
|
expectNotNil(foo3)
|
|
|
|
let foo4 = runtimeCast(anyValue, to: P1999.self)
|
|
expectNotNil(foo4)
|
|
}
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Dict value casting (SR-2911)") {
|
|
var dict: [AnyHashable: String] = [:]
|
|
dict["Key"] = "Value"
|
|
expectNotNil(dict["Key"] as? NSString)
|
|
expectNotNil(runtimeCast(dict["Key"], to: NSString.self))
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("String coercions should work on Linux (SR-12020)") {
|
|
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)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// rdar://58999120
|
|
CastsTests.test("Error -> NSError -> Protocol transitivity (SR-12095)") {
|
|
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
|
|
|
|
protocol Fruit {}
|
|
CastsTests.test("Generic type validation [SR-13812]") {
|
|
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)
|
|
}
|
|
|
|
CastsTests.test("Cast failure for Any! holding Error struct [SR-8964]") {
|
|
struct MyError: Error {}
|
|
let a: Any! = MyError()
|
|
let b: Any = a
|
|
expectTrue(b is Error)
|
|
}
|
|
|
|
CastsTests.test("Cannot cast from Any? to Existential [SR-1999]") {
|
|
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)
|
|
}
|
|
|
|
protocol A {}
|
|
CastsTests.test("Failing cast from Any to Optional<Protocol> [SR-6279]") {
|
|
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)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// Known to still be broken, but we can document the issue here
|
|
public protocol SomeProtocol {}
|
|
extension NSString: SomeProtocol {}
|
|
CastsTests.test("NSDictionary -> Dictionary casting [SR-12025]") {
|
|
// 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 SR-9047, 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 SR-9047 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)
|
|
}
|
|
|
|
// SR-14635 (aka rdar://78224322)
|
|
#if _runtime(_ObjC)
|
|
CastsTests.test("Do not overuse __SwiftValue")
|
|
.skip(.osxAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.skip(.iOSAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.skip(.iOSSimulatorAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.skip(.tvOSAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.skip(.tvOSSimulatorAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.skip(.watchOSAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.skip(.watchOSSimulatorAny("SR-14635 not yet fully enabled for Apple OSes"))
|
|
.code {
|
|
struct Bar {}
|
|
// This used to succeed because of overeager __SwiftValue
|
|
// boxing (and __SwiftValue does satisfy NSCopying)
|
|
expectFalse(Bar() is NSCopying)
|
|
expectFalse(Bar() as Any is NSCopying)
|
|
|
|
// This seems unavoidable?
|
|
// `Bar() as! AnyObject` gets boxed as a __SwiftValue,
|
|
// and __SwiftValue does conform to NSCopying
|
|
expectTrue(Bar() as! AnyObject is NSCopying)
|
|
|
|
class Foo {}
|
|
// Foo does not conform to NSCopying
|
|
// (This used to succeed due to over-eager __SwiftValue boxing)
|
|
expectFalse(Foo() is NSCopying)
|
|
expectFalse(Foo() as Any is NSCopying)
|
|
|
|
// A type that really does conform should cast to NSCopying
|
|
class Foo2: NSCopying {
|
|
func copy(with: NSZone?) -> Any { return self }
|
|
}
|
|
expectTrue(Foo2() is NSCopying)
|
|
expectTrue(Foo2() is AnyObject)
|
|
}
|
|
#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)
|
|
|
|
class Foo {}
|
|
// Any class type can be cast to AnyObject
|
|
expectTrue(Foo() is AnyObject)
|
|
}
|
|
|
|
runAllTests()
|