blob: b79b8e7d1d7eb6fb415efb4389e0017f35aa7817 [file] [log] [blame]
// RUN: %target-run-simple-swift | %FileCheck %s
// REQUIRES: executable_test
// RawRepresentable is not Equatable itself, but it does provide a generic
// implementation of == based on rawValues. This gets picked up as the
// implementation of Equatable.== when a concrete RawRepresentable type conforms
// to Equatable without providing its own implementation.
//
// However, RawRepresentable used to not provide equivalent implementations for
// hashing, allowing the compiler to synthesized hashing as usual, based on the
// actual contents of the type rather than its rawValue. Thus, the definitions
// of equality and hashing may not actually match, leading to broken hashes.
//
// The difference between rawValue and the actual contents is subtle, and it
// only causes problems in custom RawRepresentable implementations where the
// rawValue isn't actually the storage representation, like the weird struct
// below.
//
// rdar://problem/45308741
struct TrickyRawRepresentable: RawRepresentable, Hashable {
var value: [Unicode.Scalar]
var rawValue: String {
return String(String.UnicodeScalarView(value))
}
init?(rawValue: String) {
self.value = Array(rawValue.unicodeScalars)
}
}
let s1 = TrickyRawRepresentable(rawValue: "café")!
let s2 = TrickyRawRepresentable(rawValue: "cafe\u{301}")!
// CHECK: s1 == s2: true
print("s1 == s2: \(s1 == s2)")
// CHECK: hashValue matches: true
print("hashValue matches: \(s1.hashValue == s2.hashValue)")
extension Hasher {
static func hash<H: Hashable>(_ value: H) -> Int {
var hasher = Hasher()
hasher.combine(value)
return hasher.finalize()
}
}
// CHECK: hash(into:) matches: true
print("hash(into:) matches: \(Hasher.hash(s1) == Hasher.hash(s2))")
// CHECK: _rawHashValue(seed:) matches: true
let r1 = s1._rawHashValue(seed: 42)
let r2 = s2._rawHashValue(seed: 42)
print("_rawHashValue(seed:) matches: \(r1 == r2)")