@@ -175,7 +175,13 @@ AnyHashableTests.test("AnyHashable(mixed minimal hashables)/Hashable") {
175
175
% end
176
176
% end
177
177
178
- checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
178
+ checkHashable(
179
+ xs,
180
+ equalityOracle: { $0 / 2 == $1 / 2 },
181
+ // FIXME: Types that hash the same way will produce hash collisions when
182
+ // converted to AnyHashable. Arguably, the type id should be used as a hash
183
+ // discriminator.
184
+ hashEqualityOracle: { $0 / 2 % 6 == $1 / 2 % 6 })
179
185
}
180
186
181
187
% for (kw, name) in [
@@ -575,18 +581,27 @@ let interestingBitVectorArrays: [[UInt8]] = [
575
581
AnyHashableTests.test("AnyHashable(CFBitVector)/Hashable, .base") {
576
582
let bitVectors: [CFBitVector] =
577
583
interestingBitVectorArrays.map(CFBitVector.makeImmutable)
584
+ let hashEqualityOracle: (Int, Int) -> Bool = {
585
+ // CFBitVector returns its count as the hash.
586
+ interestingBitVectorArrays[$0].count == interestingBitVectorArrays[$1].count
587
+ }
578
588
let arrays = bitVectors.map { $0.asArray }
579
589
func isEq(_ lhs: [[UInt8]], _ rhs: [[UInt8]]) -> Bool {
580
590
return zip(lhs, rhs).map { $0 == $1 }.reduce(true, { $0 && $1 })
581
591
}
582
592
expectEqualTest(interestingBitVectorArrays, arrays, sameValue: isEq)
583
- checkHashable(bitVectors, equalityOracle: { $0 == $1 })
593
+ checkHashable(
594
+ bitVectors,
595
+ equalityOracle: { $0 == $1 },
596
+ hashEqualityOracle: hashEqualityOracle)
584
597
585
598
do {
586
599
expectEqual(.foreignClass, SwiftRuntime.metadataKind(of: bitVectors.first!))
587
600
588
601
let anyHashables = bitVectors.map(AnyHashable.init)
589
- checkHashable(anyHashables, equalityOracle: { $0 == $1 })
602
+ checkHashable(anyHashables,
603
+ equalityOracle: { $0 == $1 },
604
+ hashEqualityOracle: hashEqualityOracle)
590
605
591
606
let v = anyHashables.first!.base
592
607
expectTrue(type(of: v) is CFBitVector.Type)
@@ -600,7 +615,9 @@ AnyHashableTests.test("AnyHashable(CFBitVector)/Hashable, .base") {
600
615
SwiftRuntime.metadataKind(of: bitVectorsAsAnyObjects.first!))
601
616
602
617
let anyHashables = bitVectorsAsAnyObjects.map(AnyHashable.init)
603
- checkHashable(anyHashables, equalityOracle: { $0 == $1 })
618
+ checkHashable(anyHashables,
619
+ equalityOracle: { $0 == $1 },
620
+ hashEqualityOracle: hashEqualityOracle)
604
621
605
622
let v = anyHashables.first!.base
606
623
expectTrue(type(of: v) is CFBitVector.Type)
@@ -612,20 +629,30 @@ AnyHashableTests.test("AnyHashable(CFMutableBitVector)/Hashable, .base") {
612
629
// CFBitVector.
613
630
let bitVectors: [CFMutableBitVector] =
614
631
interestingBitVectorArrays.map(CFMutableBitVector.makeMutable)
632
+ let hashEqualityOracle: (Int, Int) -> Bool = {
633
+ // CFBitVector returns its count as the hash.
634
+ interestingBitVectorArrays[$0].count == interestingBitVectorArrays[$1].count
635
+ }
615
636
let arrays = bitVectors.map { $0.asArray }
616
637
func isEq(_ lhs: [[UInt8]], _ rhs: [[UInt8]]) -> Bool {
617
638
return zip(lhs, rhs).map { $0 == $1 }.reduce(true, { $0 && $1 })
618
639
}
619
640
expectEqualTest(interestingBitVectorArrays, arrays, sameValue: isEq)
620
- checkHashable(bitVectors, equalityOracle: { $0 == $1 })
641
+ checkHashable(
642
+ bitVectors,
643
+ equalityOracle: { $0 == $1 },
644
+ hashEqualityOracle: hashEqualityOracle)
621
645
622
646
do {
623
647
expectEqual(
624
648
.foreignClass,
625
649
SwiftRuntime.metadataKind(of: bitVectors.first!))
626
650
627
651
let anyHashables = bitVectors.map(AnyHashable.init)
628
- checkHashable(anyHashables, equalityOracle: { $0 == $1 })
652
+ checkHashable(
653
+ anyHashables,
654
+ equalityOracle: { $0 == $1 },
655
+ hashEqualityOracle: hashEqualityOracle)
629
656
630
657
let v = anyHashables.first!.base
631
658
expectTrue(type(of: v) is CFMutableBitVector.Type)
@@ -634,14 +661,20 @@ AnyHashableTests.test("AnyHashable(CFMutableBitVector)/Hashable, .base") {
634
661
let bitVectorsAsAnyObjects: [NSObject] = bitVectors.map {
635
662
($0 as AnyObject) as! NSObject
636
663
}
637
- checkHashable(bitVectorsAsAnyObjects, equalityOracle: { $0 == $1 })
664
+ checkHashable(
665
+ bitVectorsAsAnyObjects,
666
+ equalityOracle: { $0 == $1 },
667
+ hashEqualityOracle: hashEqualityOracle)
638
668
639
669
expectEqual(
640
670
.objCClassWrapper,
641
671
SwiftRuntime.metadataKind(of: bitVectorsAsAnyObjects.first!))
642
672
643
673
let anyHashables = bitVectorsAsAnyObjects.map(AnyHashable.init)
644
- checkHashable(anyHashables, equalityOracle: { $0 == $1 })
674
+ checkHashable(
675
+ anyHashables,
676
+ equalityOracle: { $0 == $1 },
677
+ hashEqualityOracle: hashEqualityOracle)
645
678
646
679
let v = anyHashables.first!.base
647
680
expectTrue(type(of: v) is CFMutableBitVector.Type)
@@ -717,10 +750,14 @@ AnyHashableTests.test("AnyHashable(MinimalHashableRCSwiftError)/Hashable") {
717
750
.caseC(LifetimeTracked(2)), .caseC(LifetimeTracked(2)),
718
751
]
719
752
expectEqual(.enum, SwiftRuntime.metadataKind(of: xs.first!))
720
- checkHashable(xs, equalityOracle: { $0 / 2 == $1 / 2 })
753
+ checkHashable(
754
+ xs,
755
+ equalityOracle: { $0 / 2 == $1 / 2 },
756
+ hashEqualityOracle: { $0 / 4 == $1 / 4 })
721
757
checkHashable(
722
758
xs.map(AnyHashable.init),
723
- equalityOracle: { $0 / 2 == $1 / 2 })
759
+ equalityOracle: { $0 / 2 == $1 / 2 },
760
+ hashEqualityOracle: { $0 / 4 == $1 / 4 })
724
761
}
725
762
726
763
AnyHashableTests.test("AnyHashable(MinimalHashableRCSwiftError).base") {
@@ -763,8 +800,16 @@ AnyHashableTests.test("AnyHashable(Wrappers)/Hashable") {
763
800
return lhs == rhs
764
801
}
765
802
766
- checkHashable(values, equalityOracle: equalityOracle,
767
- allowBrokenTransitivity: true)
803
+ func hashEqualityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
804
+ // Elements in [0, 3] hash the same, as do elements in [4, 7].
805
+ return lhs / 4 == rhs / 4
806
+ }
807
+
808
+ checkHashable(
809
+ values,
810
+ equalityOracle: equalityOracle,
811
+ hashEqualityOracle: hashEqualityOracle,
812
+ allowBrokenTransitivity: true)
768
813
}
769
814
770
815
AnyHashableTests.test("AnyHashable(Set)/Hashable") {
@@ -789,8 +834,10 @@ AnyHashableTests.test("AnyHashable(Set)/Hashable") {
789
834
}
790
835
}
791
836
792
- checkHashable(values, equalityOracle: equalityOracle,
793
- allowBrokenTransitivity: true)
837
+ checkHashable(
838
+ values,
839
+ equalityOracle: equalityOracle,
840
+ allowBrokenTransitivity: true)
794
841
}
795
842
796
843
AnyHashableTests.test("AnyHashable(Array)/Hashable") {
@@ -902,6 +949,12 @@ AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashableRCSwiftErr
902
949
.caseC(LifetimeTracked(1)), .caseC(LifetimeTracked(1)),
903
950
.caseC(LifetimeTracked(2)), .caseC(LifetimeTracked(2)),
904
951
]
952
+ checkHashable(
953
+ swiftErrors,
954
+ equalityOracle: { $0 / 2 == $1 / 2 },
955
+ hashEqualityOracle: { $0 / 4 == $1 / 4 },
956
+ allowBrokenTransitivity: false)
957
+
905
958
let nsErrors: [NSError] = swiftErrors.flatMap {
906
959
swiftError -> [NSError] in
907
960
let bridgedNSError = swiftError as NSError
@@ -938,16 +991,23 @@ AnyHashableTests.test("AnyHashable(_SwiftNativeNSError(MinimalHashableRCSwiftErr
938
991
return false
939
992
}
940
993
994
+ func hashEqualityOracle(_ lhs: Int, _ rhs: Int) -> Bool {
995
+ // Every NSError that has the same domain and code hash the same way.
996
+ return lhs / 8 == rhs / 8
997
+ }
998
+
941
999
// FIXME: transitivity is broken because pure `NSError`s can compare equal to
942
1000
// Swift errors with payloads just based on the domain and code, and Swift
943
1001
// errors with payloads don't compare equal when payloads differ.
944
1002
checkHashable(
945
1003
nsErrors,
946
1004
equalityOracle: equalityOracle,
1005
+ hashEqualityOracle: hashEqualityOracle,
947
1006
allowBrokenTransitivity: true)
948
1007
checkHashable(
949
1008
nsErrors.map(AnyHashable.init),
950
1009
equalityOracle: equalityOracle,
1010
+ hashEqualityOracle: hashEqualityOracle,
951
1011
allowBrokenTransitivity: true)
952
1012
953
1013
// FIXME(id-as-any): run `checkHashable` on an array of mixed
0 commit comments