Files
swift-mirror/validation-test/stdlib/AnyHashable.swift.gyb
Mark Lacey 6fa403dc7d [ConstraintSystem] Change the order in which we attempt disjunctions
to be stable.

We currently will stop visiting the elements of a disjunction under
certain circumstances once we have found a solution. The result we get
is inherently dependent on the order in which we determine to visit
the disjunctions themselves (in addition to the elements of the
disjunction).

This change makes the order in which we visit disjunctions
stable. Future commits will create a stable ordering for the elements
of disjunctions. Once we also have that stable ordering in place we
can in theory short circuit more often as part of changing the way in
which we decide what the "best" solution is to a system.

This results in an expression in
validation-test/stdlib/AnyHashable.swift.gyb no longer being able to
typecheck in a reasonable amount of time, so I had to tweak that
expression.
2018-07-16 09:32:14 -07:00

1157 lines
33 KiB
Plaintext

// 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") {
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 _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_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 _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
}
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)
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 },
hashEqualityOracle: { $0 / 4 == $1 / 4 })
let mapped = xs.map(AnyHashable.init)
checkHashable(
mapped,
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()