blob: 4a5ee24863f570e1c7c7bf8040df3c5084aaf50d [file] [log] [blame]
// RUN: %target-run-simple-swiftgyb
// REQUIRES: executable_test
// FIXME(id-as-any): make Swift errors boxed in NSError eagerly bridged.
// FIXME(id-as-any): add tests for unboxing.
// FIXME(id-as-any): add tests for the _ObjectiveCBridgeable conformance.
%{
import re
class SwiftClass(object):
def __init__(self, full_name):
m = re.match(r'([A-Za-z0-9_]+)(<(.+)>)?', full_name)
self.type_name = m.group(1)
if m.group(3) is not None:
self.generic_parameters_list = [m.group(3)]
else:
self.generic_parameters_list = []
@property
def is_generic(self):
return len(self.generic_parameters_list) != 0
@property
def generic_parameters_decl(self):
if not self.is_generic:
return ''
assert len(self.generic_parameters_list) == 1
return '<%s>' % (self.generic_parameters_list[0])
@property
def full_name(self):
return '%s%s' % (self.type_name, self.generic_parameters_decl)
def specialize_with(self, substitutions):
assert len(substitutions) == 1
assert len(self.generic_parameters_list) == 1
return SwiftClassSpecialization(
'%s<%s>' % (
self.type_name,
substitutions[self.generic_parameters_list[0]]))
class SwiftClassSpecialization(object):
def __init__(self, full_name):
m = re.match(r'(.+)(<(.+)>)?', full_name)
self.type_name = m.group(1)
self.full_name = full_name
}%
import StdlibUnittest
#if _runtime(_ObjC)
import Foundation
#endif
let AnyHashableTests = TestSuite("AnyHashableTests")
AnyHashableTests.test("CustomStringConvertible, CustomDebugStringConvertible, CustomReflectable") {
var v = AnyHashable(CustomPrintableValue(1))
expectPrinted("(value: 1).description", v)
expectDebugPrinted("AnyHashable((value: 1).debugDescription)", v)
expectDumped(
"▿ AnyHashable((value: 1).debugDescription)\n" +
" ▿ value: (value: 1).debugDescription\n" +
" - value: 1\n" +
" - identity: 0\n",
v)
}
% for wrapped in ['MinimalHashableValue', 'MinimalHashableClass']:
AnyHashableTests.test("AnyHashable(${wrapped})/Hashable") {
let xs = (0...5).flatMap {
[ ${wrapped}($0, identity: 0),
${wrapped}($0, identity: 1) ]
}
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
xs.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(${wrapped}).base") {
let ah = AnyHashable(${wrapped}(42, identity: 0))
expectEqual(${wrapped}.self, type(of: ah.base))
}
% end
#if _runtime(_ObjC)
AnyHashableTests.test("AnyHashable(MinimalHashableValue, SwiftValue(MinimalHashableValue))/Hashable") {
let xs = (0...5).flatMap {
[ MinimalHashableValue($0, identity: 0),
MinimalHashableValue($0, identity: 1) ]
}
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
let boxedXs = xs.flatMap {
[ AnyHashable($0),
AnyHashable(_bridgeAnythingToObjectiveC($0) as! NSObject) ]
}
for x in boxedXs {
expectEqual(
"MinimalHashableValue",
String(describing: type(of: x.base)))
}
checkHashable(
boxedXs,
equalityOracle: { $0 / 4 == $1 / 4 })
}
#endif
% for wrapped in ['GenericMinimalHashableValue', 'GenericMinimalHashableClass']:
% for payload in [ 'OpaqueValue<Int>', 'LifetimeTracked' ]:
AnyHashableTests.test("AnyHashable(${wrapped}<OpaqueValue<Int>>)/Hashable") {
${wrapped}_equalImpl.value = {
($0 as! ${payload}).value == ($1 as! ${payload}).value
}
${wrapped}_hashValueImpl.value = {
($0 as! ${payload}).value
}
let xs = (0...5).flatMap {
[ ${wrapped}(${payload}($0), identity: 0),
${wrapped}(${payload}($0), identity: 1) ]
}
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
xs.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(${wrapped}).base") {
let ah = AnyHashable(${wrapped}(${payload}(42), identity: 0))
expectEqual(${wrapped}<${payload}>.self, type(of: ah.base))
}
% end
% end
AnyHashableTests.test("AnyHashable(mixed minimal hashables)/Hashable") {
var xs: [AnyHashable] = []
% for wrapped in ['MinimalHashableValue', 'MinimalHashableClass']:
xs += (0...5).flatMap {
[ ${wrapped}($0, identity: 0),
${wrapped}($0, identity: 1) ].map(AnyHashable.init)
}
% end
% for wrapped in ['GenericMinimalHashableValue', 'GenericMinimalHashableClass']:
${wrapped}_equalImpl.value = {
(lhs, rhs) in
if let lhs = lhs as? OpaqueValue<Int>,
let rhs = rhs as? OpaqueValue<Int> {
return lhs.value == rhs.value
}
return (lhs as! LifetimeTracked) == (rhs as! LifetimeTracked)
}
${wrapped}_hashValueImpl.value = {
payload in
if let x = payload as? OpaqueValue<Int> {
return x.value
}
return (payload as! LifetimeTracked).value
}
% end
% for wrapped in ['GenericMinimalHashableValue', 'GenericMinimalHashableClass']:
% for payload in [ 'OpaqueValue<Int>', 'LifetimeTracked' ]:
xs += (0...5).flatMap {
[ ${wrapped}(${payload}($0), identity: 0),
${wrapped}(${payload}($0), identity: 1) ].map(AnyHashable.init)
}
% end
% end
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
}
% for (kw, name) in [
% ('class', 'Class'),
% ('struct', 'PODStruct'),
% ('struct', 'RCStruct'),
% ]:
${kw} HasCustomRepresentation_${name}
: Hashable, _HasCustomAnyHashableRepresentation
{
% if name == 'RCStruct':
var lifetimeTrackedValue: LifetimeTracked
var value: Int {
return lifetimeTrackedValue.value
}
% else:
var value: Int
% end
var identity: Int
var hasDefaultAnyHashableRepresentation: Bool
init(_ value: Int, identity: Int, hasDefaultAnyHashableRepresentation: Bool) {
% if name == 'RCStruct':
self.lifetimeTrackedValue = LifetimeTracked(value)
% else:
self.value = value
% end
self.identity = identity
self.hasDefaultAnyHashableRepresentation = hasDefaultAnyHashableRepresentation
}
var hashValue: Int {
return value
}
func _toCustomAnyHashable() -> AnyHashable? {
if hasDefaultAnyHashableRepresentation {
return nil
}
let customRepresentation =
MinimalHashableValue(value, identity: identity)
return AnyHashable(customRepresentation)
}
static func == (
lhs: HasCustomRepresentation_${name},
rhs: HasCustomRepresentation_${name}
) -> Bool {
return lhs.value == rhs.value
}
}
${kw} HasCustomRepresentation_Generic${name}<Wrapped>
: Hashable, _HasCustomAnyHashableRepresentation
{
var value: Wrapped
var identity: Int
var hasDefaultAnyHashableRepresentation: Bool
init(
_ value: Wrapped,
identity: Int,
hasDefaultAnyHashableRepresentation: Bool
) {
self.value = value
self.identity = identity
self.hasDefaultAnyHashableRepresentation = hasDefaultAnyHashableRepresentation
}
var hashValue: Int {
return asGenericMinimalHashableValue.hashValue
}
func _toCustomAnyHashable() -> AnyHashable? {
if hasDefaultAnyHashableRepresentation {
return nil
}
let customRepresentation =
GenericMinimalHashableValue(value, identity: identity)
return AnyHashable(customRepresentation)
}
var asGenericMinimalHashableValue: GenericMinimalHashableValue<Wrapped> {
return GenericMinimalHashableValue(value, identity: identity)
}
static func == <Wrapped> (
lhs: HasCustomRepresentation_Generic${name}<Wrapped>,
rhs: HasCustomRepresentation_Generic${name}<Wrapped>
) -> Bool {
return
lhs.asGenericMinimalHashableValue ==
rhs.asGenericMinimalHashableValue
}
}
% end
% for name in [ 'Class', 'PODStruct', 'RCStruct' ]:
% wrapped = 'HasCustomRepresentation_%s' % name
% genericWrapped = 'HasCustomRepresentation_Generic%s' % name
AnyHashableTests.test("AnyHashable(${wrapped})/Hashable") {
let xs = (-2...2).flatMap {
[ ${wrapped}(
$0, identity: 0,
hasDefaultAnyHashableRepresentation: $0 < 0),
${wrapped}(
$0, identity: 1,
hasDefaultAnyHashableRepresentation: $0 < 0) ]
}
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
xs.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(${wrapped}).base") {
do {
let ah = AnyHashable(
${wrapped}(
42, identity: 1,
hasDefaultAnyHashableRepresentation: true))
expectEqual(${wrapped}.self, type(of: ah.base))
}
do {
let ah = AnyHashable(
${wrapped}(
42, identity: 1,
hasDefaultAnyHashableRepresentation: false))
expectEqual(MinimalHashableValue.self, type(of: ah.base))
}
}
% for payload in [ 'OpaqueValue<Int>', 'LifetimeTracked' ]:
AnyHashableTests.test("AnyHashable(${genericWrapped}<${payload}>)/Hashable") {
GenericMinimalHashableValue_equalImpl.value = {
($0 as! ${payload}).value == ($1 as! ${payload}).value
}
GenericMinimalHashableValue_hashValueImpl.value = {
($0 as! ${payload}).value
}
let xs = (-2...2).flatMap {
[ ${genericWrapped}(
${payload}($0), identity: 0,
hasDefaultAnyHashableRepresentation: $0 < 0),
${genericWrapped}(
${payload}($0), identity: 1,
hasDefaultAnyHashableRepresentation: $0 < 0) ]
}
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
xs.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(${genericWrapped}<${payload}>)/Hashable") {
do {
let ah = AnyHashable(
${genericWrapped}(
${payload}(42), identity: 0,
hasDefaultAnyHashableRepresentation: true))
expectEqual(${genericWrapped}<${payload}>.self, type(of: ah.base))
}
do {
let ah = AnyHashable(
${genericWrapped}(
${payload}(42), identity: 0,
hasDefaultAnyHashableRepresentation: false))
expectEqual(
GenericMinimalHashableValue<${payload}>.self,
type(of: ah.base))
}
}
% end
% end
struct HasCustomRepresentationRecursively
: Hashable, _HasCustomAnyHashableRepresentation {
var value: Int
init(_ value: Int) {
self.value = value
}
var hashValue: Int {
return value
}
func _toCustomAnyHashable() -> AnyHashable? {
if value == 0 {
return AnyHashable(HasCustomRepresentationRecursively(value + 1))
} else {
return nil
}
}
static func == (
lhs: HasCustomRepresentationRecursively,
rhs: HasCustomRepresentationRecursively
) -> Bool {
return lhs.value == rhs.value
}
}
AnyHashableTests.test("AnyHashable containing values with recursive custom representations") {
GenericMinimalHashableValue_equalImpl.value = {
($0 as! ${payload}).value == ($1 as! ${payload}).value
}
GenericMinimalHashableValue_hashValueImpl.value = {
($0 as! ${payload}).value
}
// If the custom representation has its own custom representation,
// we ignore it.
let ah = AnyHashable(HasCustomRepresentationRecursively(0))
expectPrinted("HasCustomRepresentationRecursively(value: 1)", ah)
expectEqual(HasCustomRepresentationRecursively.self, type(of: ah.base))
}
class T2_Base {}
class T3_Base {}
class T4_Base1 {}
class T4_Base : T4_Base1 {}
class T5_Base1 {}
class T5_Base : T5_Base1 {}
class T6_GenericBase<T> {}
class T7_GenericBase<T> {}
class T8_GenericBase1<T> {}
class T8_GenericBase<T> : T8_GenericBase1<T> {}
class T9_GenericBase1<T> {}
class T9_GenericBase<T> : T9_GenericBase1<T> {}
#if _runtime(_ObjC)
class T10_ObjC_Base : NSObject {}
class T11_ObjC_GenericBase<T> : NSObject {}
#endif
% for prefix in [ 'T0', 'T1', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7', 'T8', 'T9', 'T10_ObjC', 'T11_ObjC' ]:
% base = None
% if prefix in ['T0', 'T1']:
% pass
% elif prefix in ['T2', 'T3', 'T4', 'T5', 'T10_ObjC']:
% base = SwiftClass(prefix + '_Base')
% elif prefix in ['T6', 'T7', 'T8', 'T9', 'T11_ObjC']:
% base = SwiftClass(prefix + '_GenericBase<T>')
% else:
% assert False
% end
% hashable_base = None
% if prefix in ['T0', 'T2', 'T4', 'T6', 'T8', 'T10_ObjC']:
% hashable_base = SwiftClass(prefix + '_HashableBase')
% elif prefix in ['T1', 'T3', 'T5', 'T7', 'T9', 'T11_ObjC']:
% hashable_base = SwiftClass(prefix + '_HashableGenericBase<T>')
% else:
% assert False
% end
% if base and base.is_generic and not hashable_base.is_generic:
% base = base.specialize_with({'T':'Int'})
% end
% bases = []
% if base:
% bases += [base]
% if not 'ObjC' in prefix:
% bases += [SwiftClass('Hashable')]
% end
${'#if _runtime(_ObjC)' if 'ObjC' in prefix else ''}
class ${hashable_base.full_name} : ${', '.join([b.full_name for b in bases])} {
var value: Int
init(_ value: Int) {
self.value = value
}
${'override' if 'ObjC' in prefix else ''} var hashValue: Int {
return value
}
% if 'ObjC' in prefix:
override func isEqual(_ object: Any?) -> Bool {
guard let rhs = object as? ${hashable_base.full_name} else {
return false
}
return self.value == rhs.value
}
% end
% if not 'ObjC' in prefix:
static func == ${hashable_base.generic_parameters_decl} (
lhs: ${hashable_base.full_name},
rhs: ${hashable_base.full_name}
) -> Bool {
return lhs.value == rhs.value
}
% end
}
%{
generic = hashable_base.generic_parameters_decl
derivedA = SwiftClass(prefix + '_DerivedA' + generic)
derivedB = SwiftClass(prefix + '_DerivedB' + generic)
derivedAA = SwiftClass(prefix + '_DerivedAA' + generic)
derivedAB = SwiftClass(prefix + '_DerivedAB' + generic)
derivedBA = SwiftClass(prefix + '_DerivedBA' + generic)
derivedBB = SwiftClass(prefix + '_DerivedBB' + generic)
derivedAAA = SwiftClass(prefix + '_DerivedAAA' + generic)
derivedAAB = SwiftClass(prefix + '_DerivedAAB' + generic)
derivedABA = SwiftClass(prefix + '_DerivedABA' + generic)
derivedABB = SwiftClass(prefix + '_DerivedABB' + generic)
derivedBAA = SwiftClass(prefix + '_DerivedBAA' + generic)
derivedBAB = SwiftClass(prefix + '_DerivedBAB' + generic)
derivedBBA = SwiftClass(prefix + '_DerivedBBA' + generic)
derivedBBB = SwiftClass(prefix + '_DerivedBBB' + generic)
types = [
(hashable_base, base),
(derivedA, hashable_base),
(derivedB, hashable_base),
(derivedAA, derivedA),
(derivedAB, derivedA),
(derivedBA, derivedB),
(derivedBB, derivedB),
(derivedAAA, derivedAA),
(derivedAAB, derivedAA),
(derivedABA, derivedAB),
(derivedABB, derivedAB),
(derivedBAA, derivedBA),
(derivedBAB, derivedBA),
(derivedBBA, derivedBB),
(derivedBBB, derivedBB),
]
}%
% for (Self, Super) in types:
% if 'Base' not in Self.type_name:
class ${Self.full_name} : ${Super.full_name} {}
% end
% end
AnyHashableTests.test("AnyHashable containing classes from the ${prefix} hierarchy") {
typealias T = Int
let xs = [
% for (i, (Self, _)) in enumerate(types):
% ConcreteSelf = Self
% if ConcreteSelf.is_generic:
% ConcreteSelf = ConcreteSelf.specialize_with({'T':'Int'})
% end
${Self.full_name}(${len(types) + i}),
% for j in range(0, len(types)):
${Self.full_name}(${j}),
% end
% end
]
func equalityOracle(_ lhs: Int, rhs: Int) -> Bool {
if lhs == rhs {
return true
}
let p = ${len(types) + 1}
if lhs % p == 0 || rhs % p == 0 {
return false
}
return lhs % p == rhs % p
}
checkHashable(xs, equalityOracle: equalityOracle)
let anyHashables = xs.map(AnyHashable.init)
checkHashable(anyHashables, equalityOracle: equalityOracle)
for (x, ah) in zip(xs, anyHashables) {
expectEqual(type(of: x), type(of: ah.base))
}
}
${'#endif' if 'ObjC' in prefix else ''}
% end
#if _runtime(_ObjC)
// There is no public way to define new CF types, so we are using
// CFBitVector and CFMutableBitVector.
extension CFBitVector {
static func makeImmutable(from values: Array<UInt8>) -> CFBitVector {
return CFBitVectorCreate(/*allocator:*/ nil, values, values.count * 8)
}
var asArray: Array<UInt8> {
var result = [UInt8](repeating: 0, count: CFBitVectorGetCount(self) / 8)
CFBitVectorGetBits(
self,
CFRange(location: 0, length: result.count * 8),
&result)
return result
}
}
extension CFMutableBitVector {
static func makeMutable(from values: Array<UInt8>) -> CFMutableBitVector {
return CFBitVectorCreateMutableCopy(
/*allocator:*/ nil,
/*capacity:*/ 0,
CFBitVector.makeImmutable(from: values))
}
}
let interestingBitVectorArrays: [[UInt8]] = [
[],
[0x00],
[0xaa],
[0xff],
[0xff, 0x00],
[0x00, 0xff],
[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
]
AnyHashableTests.test("AnyHashable(CFBitVector)/Hashable, .base") {
let bitVectors: [CFBitVector] =
interestingBitVectorArrays.map(CFBitVector.makeImmutable)
let arrays = bitVectors.map { $0.asArray }
func isEq(_ lhs: [[UInt8]], _ rhs: [[UInt8]]) -> Bool {
return zip(lhs, rhs).map { $0 == $1 }.reduce(true, { $0 && $1 })
}
expectEqualTest(interestingBitVectorArrays, arrays, sameValue: isEq)
checkHashable(bitVectors, equalityOracle: { $0 == $1 })
do {
expectEqual(.foreignClass, SwiftRuntime.metadataKind(of: bitVectors.first!))
let anyHashables = bitVectors.map(AnyHashable.init)
checkHashable(anyHashables, equalityOracle: { $0 == $1 })
let v = anyHashables.first!.base
expectTrue(type(of: v) is CFBitVector.Type)
}
do {
let bitVectorsAsAnyObjects: [NSObject] = bitVectors.map {
($0 as AnyObject) as! NSObject
}
expectEqual(
.objCClassWrapper,
SwiftRuntime.metadataKind(of: bitVectorsAsAnyObjects.first!))
let anyHashables = bitVectorsAsAnyObjects.map(AnyHashable.init)
checkHashable(anyHashables, equalityOracle: { $0 == $1 })
let v = anyHashables.first!.base
expectTrue(type(of: v) is CFBitVector.Type)
}
}
AnyHashableTests.test("AnyHashable(CFMutableBitVector)/Hashable, .base") {
// CFMutableBitVector inherits the Hashable conformance from
// CFBitVector.
let bitVectors: [CFMutableBitVector] =
interestingBitVectorArrays.map(CFMutableBitVector.makeMutable)
let arrays = bitVectors.map { $0.asArray }
func isEq(_ lhs: [[UInt8]], _ rhs: [[UInt8]]) -> Bool {
return zip(lhs, rhs).map { $0 == $1 }.reduce(true, { $0 && $1 })
}
expectEqualTest(interestingBitVectorArrays, arrays, sameValue: isEq)
checkHashable(bitVectors, equalityOracle: { $0 == $1 })
do {
expectEqual(
.foreignClass,
SwiftRuntime.metadataKind(of: bitVectors.first!))
let anyHashables = bitVectors.map(AnyHashable.init)
checkHashable(anyHashables, equalityOracle: { $0 == $1 })
let v = anyHashables.first!.base
expectTrue(type(of: v) is CFMutableBitVector.Type)
}
do {
let bitVectorsAsAnyObjects: [NSObject] = bitVectors.map {
($0 as AnyObject) as! NSObject
}
checkHashable(bitVectorsAsAnyObjects, equalityOracle: { $0 == $1 })
expectEqual(
.objCClassWrapper,
SwiftRuntime.metadataKind(of: bitVectorsAsAnyObjects.first!))
let anyHashables = bitVectorsAsAnyObjects.map(AnyHashable.init)
checkHashable(anyHashables, equalityOracle: { $0 == $1 })
let v = anyHashables.first!.base
expectTrue(type(of: v) is CFMutableBitVector.Type)
}
}
#endif
enum MinimalHashablePODSwiftError : Error, Hashable {
case caseA
case caseB
case caseC
}
enum MinimalHashableRCSwiftError : Error, Hashable {
case caseA(LifetimeTracked)
case caseB(LifetimeTracked)
case caseC(LifetimeTracked)
var hashValue: Int {
switch self {
case .caseA:
return 10
case .caseB:
return 20
case .caseC:
return 30
}
}
static func == (
lhs: MinimalHashableRCSwiftError,
rhs: MinimalHashableRCSwiftError
) -> Bool {
switch (lhs, rhs) {
case (.caseA(let lhs), .caseA(let rhs)):
return lhs == rhs
case (.caseB(let lhs), .caseB(let rhs)):
return lhs == rhs
case (.caseC(let lhs), .caseC(let rhs)):
return lhs == rhs
default:
return false
}
}
}
AnyHashableTests.test("AnyHashable(MinimalHashablePODSwiftError)/Hashable") {
let xs: [MinimalHashablePODSwiftError] = [
.caseA, .caseA,
.caseB, .caseB,
.caseC, .caseC,
]
expectEqual(.enum, SwiftRuntime.metadataKind(of: xs.first!))
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
xs.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(MinimalHashablePODSwiftError).base") {
let ah = AnyHashable(MinimalHashablePODSwiftError.caseA)
expectEqual(MinimalHashablePODSwiftError.self, type(of: ah.base))
}
AnyHashableTests.test("AnyHashable(MinimalHashableRCSwiftError)/Hashable") {
let xs: [MinimalHashableRCSwiftError] = [
.caseA(LifetimeTracked(1)), .caseA(LifetimeTracked(1)),
.caseA(LifetimeTracked(2)), .caseA(LifetimeTracked(2)),
.caseB(LifetimeTracked(1)), .caseB(LifetimeTracked(1)),
.caseB(LifetimeTracked(2)), .caseB(LifetimeTracked(2)),
.caseC(LifetimeTracked(1)), .caseC(LifetimeTracked(1)),
.caseC(LifetimeTracked(2)), .caseC(LifetimeTracked(2)),
]
expectEqual(.enum, SwiftRuntime.metadataKind(of: xs.first!))
checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
xs.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(MinimalHashableRCSwiftError).base") {
let ah = AnyHashable(MinimalHashableRCSwiftError.caseA(LifetimeTracked(1)))
expectEqual(MinimalHashableRCSwiftError.self, type(of: ah.base))
}
#if _runtime(_ObjC)
// 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
}
AnyHashableTests.test("AnyHashable(Wrappers)/Hashable") {
let values: [AnyHashable] = [
StringWrapper1(rawValue: "hello"),
StringWrapper2(rawValue: "hello"),
"hello" as String,
"hello" as NSString,
StringWrapper1(rawValue: "world"),
StringWrapper2(rawValue: "world"),
"world" as String,
"world" as NSString,
]
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
// Elements in [0, 3] match 3.
if lhs == 3 { return rhs >= 0 && rhs <= 3 }
if rhs == 3 { return lhs >= 0 && lhs <= 3 }
// Elements in [4, 7] match 7.
if lhs == 7 { return rhs >= 4 && rhs <= 7 }
if rhs == 7 { return lhs >= 4 && lhs <= 7 }
return lhs == rhs
}
checkHashable(values, equalityOracle: equalityOracle,
allowBrokenTransitivity: true)
}
AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashablePODSwiftError))/Hashable") {
let swiftErrors: [MinimalHashablePODSwiftError] = [
.caseA, .caseA,
.caseB, .caseB,
.caseC, .caseC,
]
let nsErrors: [NSError] = swiftErrors.flatMap {
swiftError -> [NSError] in
let bridgedNSError = swiftError as NSError
return [
bridgedNSError,
NSError(domain: bridgedNSError.domain, code: bridgedNSError.code)
]
}
expectEqual(
.objCClassWrapper,
SwiftRuntime.metadataKind(of: nsErrors[0]))
expectEqual("_SwiftNativeNSError", String(describing: type(of: nsErrors[0])))
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
// Swift errors compare equal to the `NSError`s that have the same domain
// and code.
return lhs / 4 == rhs / 4
}
checkHashable(nsErrors, equalityOracle: equalityOracle)
checkHashable(
nsErrors.map(AnyHashable.init),
equalityOracle: equalityOracle)
// FIXME(id-as-any): run `checkHashable` on an array of mixed
// `AnyHashable(MinimalHashablePODSwiftError)` and
// `AnyHashable(_SwiftNativeNSError(MinimalHashablePODSwiftError))`. For
// this to succeed, we need to eagerly bridge Swift errors into the Swift
// representation when wrapped in `AnyHashable`.
}
AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashablePODSwiftError)).base") {
let ah = AnyHashable(MinimalHashablePODSwiftError.caseA as NSError)
expectTrue(type(of: ah.base) is NSError.Type)
}
AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashableRCSwiftError))/Hashable") {
let swiftErrors: [MinimalHashableRCSwiftError] = [
.caseA(LifetimeTracked(1)), .caseA(LifetimeTracked(1)),
.caseA(LifetimeTracked(2)), .caseA(LifetimeTracked(2)),
.caseB(LifetimeTracked(1)), .caseB(LifetimeTracked(1)),
.caseB(LifetimeTracked(2)), .caseB(LifetimeTracked(2)),
.caseC(LifetimeTracked(1)), .caseC(LifetimeTracked(1)),
.caseC(LifetimeTracked(2)), .caseC(LifetimeTracked(2)),
]
let nsErrors: [NSError] = swiftErrors.flatMap {
swiftError -> [NSError] in
let bridgedNSError = swiftError as NSError
return [
bridgedNSError,
NSError(domain: bridgedNSError.domain, code: bridgedNSError.code)
]
}
expectEqual(
.objCClassWrapper,
SwiftRuntime.metadataKind(of: nsErrors[0]))
expectEqual("_SwiftNativeNSError", String(describing: type(of: nsErrors[0])))
expectEqual(
.objCClassWrapper,
SwiftRuntime.metadataKind(of: nsErrors[1]))
expectEqual("NSError", String(describing: type(of: nsErrors[1])))
func equalityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
// Equality of bridged Swift errors takes the payload into account, so for
// a fixed X, Y, all `.caseX(LifetimeTracked(Y))` compare equal.
// They also compare equal to the `NSError`s that have the same domain
// and code.
if lhs / 4 == rhs / 4 {
return true
}
// `NSError`s that have the same domain and code as a bridged Swift
// error compare equal to all Swift errors with the same domain and code
// regardless of the payload.
if (lhs % 2 == 1 || rhs % 2 == 1) && (lhs / 8 == rhs / 8) {
return true
}
return false
}
// FIXME: transitivity is broken because pure `NSError`s can compare equal to
// Swift errors with payloads just based on the domain and code, and Swift
// errors with payloads don't compare equal when payloads differ.
checkHashable(
nsErrors,
equalityOracle: equalityOracle,
allowBrokenTransitivity: true)
checkHashable(
nsErrors.map(AnyHashable.init),
equalityOracle: equalityOracle,
allowBrokenTransitivity: true)
// FIXME(id-as-any): run `checkHashable` on an array of mixed
// `AnyHashable(MinimalHashableRCSwiftError)` and
// `AnyHashable(_SwiftNativeNSError(MinimalHashableRCSwiftError))`. For
// this to succeed, we need to eagerly bridge Swift errors into the Swift
// representation when wrapped in `AnyHashable`.
}
AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashableRCSwiftError)).base") {
let ah = AnyHashable(
MinimalHashableRCSwiftError.caseA(LifetimeTracked(1)) as NSError)
expectTrue(type(of: ah.base) is NSError.Type)
}
AnyHashableTests.test("AnyHashable(NSError)/Hashable") {
let nsErrors: [NSError] = [
NSError(domain: "Foo", code: 0),
NSError(domain: "Foo", code: 0),
NSError(domain: "Foo", code: 1),
NSError(domain: "Foo", code: 1),
NSError(domain: "Foo", code: 2),
NSError(domain: "Foo", code: 2),
]
expectEqual(
.objCClassWrapper,
SwiftRuntime.metadataKind(of: nsErrors.first!))
checkHashable(nsErrors, equalityOracle: { $0 / 2 == $1 / 2 })
checkHashable(
nsErrors.map(AnyHashable.init),
equalityOracle: { $0 / 2 == $1 / 2 })
}
AnyHashableTests.test("AnyHashable(NSError).base") {
let ah = AnyHashable(NSError(domain: "Foo", code: 0))
expectTrue(type(of: ah.base) is NSError.Type)
}
#endif
runAllTests()