mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
* spelling: abcdefghijklmnopqrstuvwxyz Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: clazz Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: collection Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: compressible Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: constituent Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: contiguous Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: convertibility Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: element Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: enforce Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: exhaustive Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: exhausts Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: existential Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: facilitate Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: ignored Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: incorporated Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: intersection Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: laziness Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: misaligned Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: overhaul Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: preamble Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: precondition Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: replacement Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: trailing Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: unambiguous Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: uncompressible Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> * spelling: world Signed-off-by: Josh Soref <jsoref@users.noreply.github.com> Co-authored-by: Josh Soref <jsoref@users.noreply.github.com>
275 lines
8.2 KiB
Swift
275 lines
8.2 KiB
Swift
// RUN: %empty-directory(%t)
|
|
//
|
|
// RUN: %gyb %s -o %t/AnyHashableCasts.swift
|
|
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-build-swift -g -module-name a %t/AnyHashableCasts.swift -o %t.out
|
|
// RUN: %target-codesign %t.out
|
|
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-run %t.out
|
|
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-build-swift -g -O -module-name a %t/AnyHashableCasts.swift -o %t.out.optimized
|
|
// RUN: %target-codesign %t.out.optimized
|
|
// RUN: %line-directive %t/AnyHashableCasts.swift -- %target-run %t.out.optimized
|
|
// REQUIRES: executable_test
|
|
|
|
import StdlibUnittest
|
|
|
|
#if _runtime(_ObjC)
|
|
import Foundation
|
|
#endif
|
|
|
|
var AnyHashableCasts = TestSuite("AnyHashableCasts")
|
|
|
|
protocol Implemented {}
|
|
protocol Unimplemented {}
|
|
extension Int : Implemented {}
|
|
|
|
struct HashableStruct : Hashable {
|
|
var value : Int
|
|
static func ==(lhs: HashableStruct, rhs: HashableStruct) -> Bool {
|
|
return lhs.value == rhs.value
|
|
}
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(value)
|
|
}
|
|
}
|
|
|
|
class HashableClass : Hashable {
|
|
var value : Int
|
|
init(value v: Int) { self.value = v }
|
|
static func ==(lhs: HashableClass, rhs: HashableClass) -> Bool {
|
|
return lhs.value == rhs.value
|
|
}
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(value)
|
|
}
|
|
}
|
|
|
|
enum HashableEnum : Hashable {
|
|
case value(Int)
|
|
static func ==(lhs: HashableEnum, rhs: HashableEnum) -> Bool {
|
|
switch (lhs, rhs) {
|
|
case (.value(let l), .value(let r)): return l == r
|
|
}
|
|
}
|
|
func hash(into hasher: inout Hasher) {
|
|
switch self {
|
|
case .value(let v):
|
|
hasher.combine(v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func opaqueCast<T, U>(_ lhs: T, _ rhs: U.Type) -> U? {
|
|
return lhs as? U
|
|
}
|
|
|
|
func matches<T: Equatable>(_ lhs: T, _ rhs: T) -> Bool {
|
|
return lhs == rhs
|
|
}
|
|
|
|
func matches<T: Equatable, U>(_ lhs: U, _ rhs: T) -> Bool {
|
|
if let x = lhs as? T {
|
|
return x == rhs
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
%{
|
|
testCases = [
|
|
("5", "AnyHashable", "Int", "5"),
|
|
("5", "AnyHashable", "Any", "5"),
|
|
("5", "AnyHashable", "Implemented", "5"),
|
|
("5", "AnyHashable", "Unimplemented", False),
|
|
("5", "Int", "AnyHashable", "5"),
|
|
("5", "Any", "AnyHashable", "5"),
|
|
("AnyHashable(5)", "Any", "Int", "5"),
|
|
("HashableStruct(value: 5)", "HashableStruct", "AnyHashable",
|
|
"AnyHashable(HashableStruct(value: 5))"),
|
|
("HashableStruct(value: 5)", "AnyHashable", "HashableStruct",
|
|
"AnyHashable(HashableStruct(value: 5))"),
|
|
("HashableClass(value: 5)", "HashableClass", "AnyHashable",
|
|
"AnyHashable(HashableClass(value: 5))"),
|
|
("HashableClass(value: 5)", "AnyHashable", "HashableClass",
|
|
"AnyHashable(HashableClass(value: 5))"),
|
|
("HashableEnum.value(5)", "HashableEnum", "AnyHashable",
|
|
"AnyHashable(HashableEnum.value(5))"),
|
|
("HashableEnum.value(5)", "AnyHashable", "HashableEnum",
|
|
"AnyHashable(HashableEnum.value(5))"),
|
|
]
|
|
}%
|
|
|
|
% for valueExpr, coercedType, castType, expected in testCases:
|
|
AnyHashableCasts.test("${valueExpr} as ${coercedType} as? ${castType}") {
|
|
do {
|
|
let x = ${valueExpr}
|
|
let y : ${coercedType} = x
|
|
if let z = y as? ${castType} {
|
|
%if expected:
|
|
expectTrue(matches(z, ${expected}))
|
|
%else:
|
|
expectUnreachable()
|
|
%end
|
|
} else {
|
|
%if expected:
|
|
expectUnreachable()
|
|
%end
|
|
}
|
|
|
|
if let z = opaqueCast(y, (${castType}).self) {
|
|
%if expected:
|
|
expectTrue(matches(z, ${expected}))
|
|
%else:
|
|
expectUnreachable()
|
|
%end
|
|
} else {
|
|
%if expected:
|
|
expectUnreachable()
|
|
%end
|
|
}
|
|
}
|
|
}
|
|
% end
|
|
|
|
AnyHashableCasts.test("Casting to AnyHashable doesn't leak") {
|
|
do {
|
|
let tracked = LifetimeTracked(42)
|
|
let anyHashable = AnyHashable(tracked)
|
|
let anyObject = anyHashable as AnyObject
|
|
_ = anyObject as? AnyHashable
|
|
}
|
|
expectEqual(LifetimeTracked.instances, 0)
|
|
}
|
|
|
|
#if _runtime(_ObjC)
|
|
// A wrapper type around Int that bridges to NSNumber.
|
|
struct IntWrapper1: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: Int
|
|
}
|
|
|
|
// A wrapper type around Int that bridges to NSNumber.
|
|
struct IntWrapper2: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: Int
|
|
}
|
|
|
|
AnyHashableCasts.test("Wrappers around bridged integers") {
|
|
let wrapper1: AnyHashable = IntWrapper1(rawValue: 42)
|
|
let wrapper2: AnyHashable = IntWrapper2(rawValue: 42)
|
|
let integer: AnyHashable = 42 as Int
|
|
let byte: AnyHashable = 42 as UInt8
|
|
let double: AnyHashable = 42.0 as Double
|
|
let number: AnyHashable = 42 as NSNumber
|
|
|
|
// Wrappers compare equal to their wrapped value as AnyHashable.
|
|
expectEqual(wrapper1, wrapper2)
|
|
expectEqual(wrapper1, integer)
|
|
expectEqual(wrapper1, byte)
|
|
expectEqual(wrapper1, double)
|
|
expectEqual(wrapper1, number)
|
|
|
|
// Original types are preserved in the base property.
|
|
expectTrue(wrapper1.base is IntWrapper1)
|
|
expectTrue(wrapper2.base is IntWrapper2)
|
|
expectTrue(integer.base is Int)
|
|
expectTrue(byte.base is UInt8)
|
|
expectTrue(double.base is Double)
|
|
expectTrue(number.base is NSNumber) // Through bridging
|
|
|
|
// AnyHashable forms can be casted to any standard numeric type that can hold
|
|
// their value.
|
|
expectNotNil(wrapper1 as? IntWrapper1)
|
|
expectNotNil(wrapper1 as? IntWrapper2)
|
|
expectNotNil(wrapper1 as? Int)
|
|
expectNotNil(wrapper1 as? UInt8)
|
|
expectNotNil(wrapper1 as? Double)
|
|
expectNotNil(wrapper1 as? NSNumber)
|
|
|
|
expectNotNil(byte as? IntWrapper1)
|
|
expectNotNil(byte as? IntWrapper2)
|
|
expectNotNil(byte as? Int)
|
|
expectNotNil(byte as? UInt8)
|
|
expectNotNil(byte as? Double)
|
|
expectNotNil(byte as? NSNumber)
|
|
|
|
expectNotNil(integer as? IntWrapper1)
|
|
expectNotNil(integer as? IntWrapper2)
|
|
expectNotNil(integer as? Int)
|
|
expectNotNil(integer as? UInt8)
|
|
expectNotNil(integer as? Double)
|
|
expectNotNil(integer as? NSNumber)
|
|
|
|
expectNotNil(double as? IntWrapper1)
|
|
expectNotNil(double as? IntWrapper2)
|
|
expectNotNil(double as? Int)
|
|
expectNotNil(double as? UInt8)
|
|
expectNotNil(double as? Double)
|
|
expectNotNil(double as? NSNumber)
|
|
|
|
expectNotNil(number as? IntWrapper1)
|
|
expectNotNil(number as? IntWrapper2)
|
|
expectNotNil(number as? Int)
|
|
expectNotNil(number as? UInt8)
|
|
expectNotNil(number as? Double)
|
|
expectNotNil(number as? NSNumber)
|
|
|
|
// We can't cast to a numeric type that can't hold the value.
|
|
let big: AnyHashable = Int32.max
|
|
expectNotNil(big as? IntWrapper1)
|
|
expectNotNil(big as? IntWrapper2)
|
|
expectNotNil(big as? Int)
|
|
expectNil(big as? UInt8) // <--
|
|
expectNotNil(big as? Double)
|
|
expectNotNil(big as? NSNumber)
|
|
}
|
|
|
|
// A wrapper type around a String that bridges to NSString.
|
|
struct StringWrapper1: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: String
|
|
}
|
|
|
|
// A wrapper type around a String that bridges to NSString.
|
|
struct StringWrapper2: _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: String
|
|
}
|
|
|
|
AnyHashableCasts.test("Wrappers around bridged strings") {
|
|
let wrapper1Hello: AnyHashable = StringWrapper1(rawValue: "hello")
|
|
let wrapper2Hello: AnyHashable = StringWrapper2(rawValue: "hello")
|
|
let stringHello: AnyHashable = "hello" as String
|
|
let nsStringHello: AnyHashable = "hello" as NSString
|
|
|
|
// Wrappers compare equal to their wrapped value as AnyHashable.
|
|
expectEqual(wrapper1Hello, wrapper2Hello)
|
|
expectEqual(wrapper1Hello, stringHello)
|
|
expectEqual(wrapper1Hello, nsStringHello)
|
|
expectEqual(wrapper2Hello, stringHello)
|
|
expectEqual(wrapper2Hello, nsStringHello)
|
|
expectEqual(stringHello, nsStringHello)
|
|
|
|
// Type identity is maintained through the base property.
|
|
expectTrue(wrapper1Hello.base is StringWrapper1)
|
|
expectTrue(wrapper2Hello.base is StringWrapper2)
|
|
expectTrue(stringHello.base is String)
|
|
expectTrue(nsStringHello.base is NSString) // Through bridging
|
|
|
|
// Swift wrapper's AnyHashable form doesn't enforce type identity.
|
|
expectNotNil(wrapper1Hello as? StringWrapper1)
|
|
expectNotNil(wrapper1Hello as? StringWrapper2)
|
|
expectNotNil(wrapper1Hello as? String)
|
|
expectNotNil(wrapper1Hello as? NSString)
|
|
|
|
// String's AnyHashable form doesn't enforce type identity.
|
|
expectNotNil(stringHello as? StringWrapper1)
|
|
expectNotNil(stringHello as? StringWrapper2)
|
|
expectNotNil(stringHello as? String)
|
|
expectNotNil(stringHello as? NSString)
|
|
|
|
// NSString's AnyHashable form doesn't enforce type identity.
|
|
expectNotNil(nsStringHello as? StringWrapper1)
|
|
expectNotNil(nsStringHello as? StringWrapper2)
|
|
expectNotNil(nsStringHello as? String)
|
|
expectNotNil(nsStringHello as? NSString)
|
|
}
|
|
|
|
#endif
|
|
|
|
runAllTests()
|