mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
1169 lines
33 KiB
Swift
1169 lines
33 KiB
Swift
// RUN: %target-run-simple-swiftgyb
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: reflection
|
|
|
|
// 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") {
|
|
let 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}_hashIntoImpl.value = {
|
|
$1.combine(($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..<6).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}_hashIntoImpl.value = { payload, hasher in
|
|
if let x = payload as? OpaqueValue<Int> {
|
|
hasher.combine(x.value)
|
|
return
|
|
}
|
|
hasher.combine((payload as! LifetimeTracked).value)
|
|
}
|
|
% end
|
|
|
|
% for wrapped in ['GenericMinimalHashableValue', 'GenericMinimalHashableClass']:
|
|
% for payload in [ 'OpaqueValue<Int>', 'LifetimeTracked' ]:
|
|
xs += (0..<6).flatMap {
|
|
[ ${wrapped}(${payload}($0), identity: 0),
|
|
${wrapped}(${payload}($0), identity: 1) ].map(AnyHashable.init)
|
|
}
|
|
% end
|
|
% end
|
|
|
|
checkHashable(
|
|
xs,
|
|
equalityOracle: { $0 / 2 == $1 / 2 },
|
|
// FIXME: Types that hash the same way will produce hash collisions when
|
|
// converted to AnyHashable. Arguably, the type id should be used as a hash
|
|
// discriminator.
|
|
hashEqualityOracle: { $0 / 2 % 6 == $1 / 2 % 6 })
|
|
}
|
|
|
|
% 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 hash(into hasher: inout Hasher) {
|
|
hasher.combine(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 hash(into hasher: inout Hasher) {
|
|
hasher.combine(asGenericMinimalHashableValue)
|
|
}
|
|
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_hashIntoImpl.value = { v, hasher in
|
|
hasher.combine((v 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 hash(into hasher: inout Hasher) {
|
|
hasher.combine(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_hashIntoImpl.value = { v, hasher in
|
|
hasher.combine((v 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
|
|
}
|
|
% if 'ObjC' in prefix:
|
|
override var hash: Int {
|
|
return value
|
|
}
|
|
override func isEqual(_ object: Any?) -> Bool {
|
|
guard let rhs = object as? ${hashable_base.full_name} else {
|
|
return false
|
|
}
|
|
return self.value == rhs.value
|
|
}
|
|
% else:
|
|
var hashValue: Int {
|
|
return value
|
|
}
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(value)
|
|
}
|
|
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
|
|
% bunch = 2
|
|
let xs = [
|
|
% for (i, (Self, _)) in enumerate(types):
|
|
% ConcreteSelf = Self
|
|
% if ConcreteSelf.is_generic:
|
|
% ConcreteSelf = ConcreteSelf.specialize_with({'T':'Int'})
|
|
% end
|
|
${Self.full_name}(${bunch + i}),
|
|
% for j in range(0, bunch):
|
|
${Self.full_name}(${j}),
|
|
% end
|
|
% end
|
|
]
|
|
func equalityOracle(_ lhs: Int, rhs: Int) -> Bool {
|
|
if lhs == rhs {
|
|
return true
|
|
}
|
|
let p = ${bunch + 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 hashEqualityOracle: (Int, Int) -> Bool = {
|
|
// CFBitVector returns its count as the hash.
|
|
interestingBitVectorArrays[$0].count == interestingBitVectorArrays[$1].count
|
|
}
|
|
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 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
do {
|
|
expectEqual("ForeignClass", SwiftRuntime.metadataKind(of: bitVectors.first!))
|
|
|
|
let anyHashables = bitVectors.map(AnyHashable.init)
|
|
checkHashable(anyHashables,
|
|
equalityOracle: { $0 == $1 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
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 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
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 hashEqualityOracle: (Int, Int) -> Bool = {
|
|
// CFBitVector returns its count as the hash.
|
|
interestingBitVectorArrays[$0].count == interestingBitVectorArrays[$1].count
|
|
}
|
|
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 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
do {
|
|
expectEqual(
|
|
"ForeignClass",
|
|
SwiftRuntime.metadataKind(of: bitVectors.first!))
|
|
|
|
let anyHashables = bitVectors.map(AnyHashable.init)
|
|
checkHashable(
|
|
anyHashables,
|
|
equalityOracle: { $0 == $1 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
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 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
expectEqual(
|
|
"ObjCClassWrapper",
|
|
SwiftRuntime.metadataKind(of: bitVectorsAsAnyObjects.first!))
|
|
|
|
let anyHashables = bitVectorsAsAnyObjects.map(AnyHashable.init)
|
|
checkHashable(
|
|
anyHashables,
|
|
equalityOracle: { $0 == $1 },
|
|
hashEqualityOracle: hashEqualityOracle)
|
|
|
|
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)
|
|
|
|
func hash(into hasher: inout Hasher) {
|
|
switch self {
|
|
case .caseA:
|
|
hasher.combine(10)
|
|
case .caseB:
|
|
hasher.combine(20)
|
|
case .caseC:
|
|
hasher.combine(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 },
|
|
hashEqualityOracle: { $0 / 4 == $1 / 4 })
|
|
checkHashable(
|
|
xs.map(AnyHashable.init),
|
|
equalityOracle: { $0 / 2 == $1 / 2 },
|
|
hashEqualityOracle: { $0 / 4 == $1 / 4 })
|
|
}
|
|
|
|
AnyHashableTests.test("AnyHashable(MinimalHashableRCSwiftError).base") {
|
|
let ah = AnyHashable(MinimalHashableRCSwiftError.caseA(LifetimeTracked(1)))
|
|
expectEqual(MinimalHashableRCSwiftError.self, type(of: ah.base))
|
|
}
|
|
|
|
AnyHashableTests.test("AnyHashable(NumericTypes)/Hashable") {
|
|
// Numeric types holding mathematically equal values must compare equal and
|
|
// hash the same way when converted to AnyHashable.
|
|
let groups: [[AnyHashable]] = [
|
|
[
|
|
1 as Int,
|
|
1 as UInt,
|
|
1 as Int8,
|
|
1 as UInt8,
|
|
1 as Int16,
|
|
1 as UInt16,
|
|
1 as Int32,
|
|
1 as UInt32,
|
|
1 as Int64,
|
|
1 as UInt64,
|
|
1 as Float,
|
|
1 as Double,
|
|
],
|
|
[
|
|
42 as Int,
|
|
42 as UInt,
|
|
42 as Int8,
|
|
42 as UInt8,
|
|
42 as Int16,
|
|
42 as UInt16,
|
|
42 as Int32,
|
|
42 as UInt32,
|
|
42 as Int64,
|
|
42 as UInt64,
|
|
42 as Float,
|
|
42 as Double,
|
|
],
|
|
[
|
|
Int(Int32.max),
|
|
UInt(Int32.max),
|
|
Int32.max,
|
|
UInt32(Int32.max),
|
|
Int64(Int32.max),
|
|
UInt64(Int32.max),
|
|
Double(Int32.max),
|
|
],
|
|
[
|
|
Float.infinity,
|
|
Double.infinity,
|
|
],
|
|
[
|
|
0x1.aP1 as Float, // 3.25
|
|
0x1.aP1 as Double,
|
|
],
|
|
[
|
|
0x1.a000000000001P1, // 3.25.nextUp, not representable by a Float
|
|
]
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
|
|
#if !os(Windows) && (arch(i386) || arch(x86_64))
|
|
AnyHashableTests.test("AnyHashable(Float80)/Hashable") {
|
|
let groups: [[AnyHashable]] = [
|
|
[
|
|
42 as Int,
|
|
42 as Float,
|
|
42 as Double,
|
|
42 as Float80,
|
|
],
|
|
[
|
|
Float.infinity,
|
|
Double.infinity,
|
|
Float80.infinity,
|
|
],
|
|
[
|
|
3.25 as Float,
|
|
3.25 as Double,
|
|
3.25 as Float80,
|
|
],
|
|
[
|
|
0x1.a000000000001P1 as Double, // 3.25.nextUp
|
|
0x1.a000000000001P1 as Float80,
|
|
],
|
|
[
|
|
0x1.a000000000000002p1 as Float80, // (3.25 as Float80).nextUp
|
|
],
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
#endif
|
|
|
|
#if _runtime(_ObjC)
|
|
// A wrapper type around an Int that bridges to NSNumber.
|
|
struct IntWrapper1 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: Int
|
|
}
|
|
|
|
// A wrapper type around an Int that bridges to NSNumber.
|
|
struct IntWrapper2 : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: Int
|
|
}
|
|
|
|
// A wrapper type around an Int that bridges to NSNumber.
|
|
struct Int8Wrapper : _SwiftNewtypeWrapper, Hashable, _ObjectiveCBridgeable {
|
|
let rawValue: Int8
|
|
}
|
|
|
|
AnyHashableTests.test("AnyHashable(IntWrappers)/Hashable") {
|
|
let groups: [[AnyHashable]] = [
|
|
[
|
|
IntWrapper1(rawValue: 42),
|
|
IntWrapper2(rawValue: 42),
|
|
Int8Wrapper(rawValue: 42),
|
|
42,
|
|
42 as Double,
|
|
42 as NSNumber,
|
|
],
|
|
[
|
|
IntWrapper1(rawValue: -23),
|
|
IntWrapper2(rawValue: -23),
|
|
Int8Wrapper(rawValue: -23),
|
|
-23,
|
|
-23 as Double,
|
|
-23 as NSNumber,
|
|
],
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
#endif
|
|
|
|
#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(StringWrappers)/Hashable") {
|
|
let groups: [[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,
|
|
]
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
|
|
AnyHashableTests.test("AnyHashable(Set)/Hashable") {
|
|
let groups: [[AnyHashable]] = [
|
|
[
|
|
Set([1, 2, 3]),
|
|
Set([1, 2, 3] as [Int8]),
|
|
Set([1, 2, 3] as [Float]),
|
|
NSSet(set: [1, 2, 3]),
|
|
],
|
|
[
|
|
Set([2, 3, 4]),
|
|
NSSet(set: [2, 3, 4]),
|
|
],
|
|
[
|
|
Set([Set([1, 2]), Set([3, 4])]),
|
|
NSSet(set: [NSSet(set: [1, 2]), NSSet(set: [3, 4])]),
|
|
],
|
|
[
|
|
Set([Set([1, 3]), Set([2, 4])]),
|
|
NSSet(set: [NSSet(set: [1, 3]), NSSet(set: [2, 4])]),
|
|
],
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
|
|
AnyHashableTests.test("AnyHashable(Array)/Hashable") {
|
|
let groups: [[AnyHashable]] = [
|
|
[
|
|
[1, 2, 3],
|
|
[1, 2, 3] as [Int8],
|
|
[1, 2, 3] as [Double],
|
|
NSArray(array: [1, 2, 3]),
|
|
],
|
|
[
|
|
[3, 2, 1],
|
|
[3, 2, 1] as [AnyHashable],
|
|
NSArray(array: [3, 2, 1]),
|
|
],
|
|
[
|
|
[[1, 2], [3, 4]],
|
|
NSArray(array: [NSArray(array: [1, 2]), NSArray(array: [3, 4])]),
|
|
],
|
|
[
|
|
[[3, 4], [1, 2]],
|
|
NSArray(array: [NSArray(array: [3, 4]), NSArray(array: [1, 2])]),
|
|
]
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
|
|
AnyHashableTests.test("AnyHashable(Dictionary)/Hashable") {
|
|
let groups: [[AnyHashable]] = [
|
|
[
|
|
["hello": 1, "world": 2] as [String: Int],
|
|
["hello": 1, "world": 2] as [String: Int16],
|
|
["hello": 1, "world": 2] as [String: Float],
|
|
NSDictionary(dictionary: ["hello": 1, "world": 2]),
|
|
],
|
|
[
|
|
["hello": 2, "world": 1],
|
|
NSDictionary(dictionary: ["hello": 2, "world": 1]),
|
|
],
|
|
[
|
|
["hello": ["foo": 1, "bar": 2],
|
|
"world": ["foo": 2, "bar": 1]],
|
|
NSDictionary(dictionary: [
|
|
"hello": ["foo": 1, "bar": 2],
|
|
"world": ["foo": 2, "bar": 1]]),
|
|
],
|
|
[
|
|
["hello": ["foo": 2, "bar": 1],
|
|
"world": ["foo": 1, "bar": 2]],
|
|
NSDictionary(dictionary: [
|
|
"hello": ["foo": 2, "bar": 1],
|
|
"world": ["foo": 1, "bar": 2]]),
|
|
],
|
|
]
|
|
checkHashableGroups(groups)
|
|
}
|
|
|
|
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)),
|
|
]
|
|
checkHashable(
|
|
swiftErrors,
|
|
equalityOracle: { $0 / 2 == $1 / 2 },
|
|
hashEqualityOracle: { $0 / 4 == $1 / 4 },
|
|
allowBrokenTransitivity: false)
|
|
|
|
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
|
|
}
|
|
|
|
func hashEqualityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
|
|
// Every NSError that has the same domain and code hash the same way.
|
|
return lhs / 8 == rhs / 8
|
|
}
|
|
|
|
// 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,
|
|
hashEqualityOracle: hashEqualityOracle,
|
|
allowBrokenTransitivity: true)
|
|
checkHashable(
|
|
nsErrors.map(AnyHashable.init),
|
|
equalityOracle: equalityOracle,
|
|
hashEqualityOracle: hashEqualityOracle,
|
|
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()
|
|
|