File: Mixin%2BHash.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (63 lines) | stat: -rw-r--r-- 2,773 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2021 Apple Inc. and the Swift project authors
 Licensed under Apache License v2.0 with Runtime Library Exception

 See https://swift.org/LICENSE.txt for license information
 See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation

// `Mixin` does not conform to `Hashable` right now primarily because
// this would complicate its usage in many situations because of "Self
// or associated type" requirements errors. `Hashable` inherits those
// frome `Equatable`, even though its primary functionality, the `hash(into:)`
// function has no Self or associated type requirements. Thus, in order to
// access a `Mixin`'s `hash(into:)` function, we need to somehow get access to
// the `Mixin`'s `Hashable` conformance.
//
// Note that all of this would be significantly easier in Swift 5.7, so
// it might be worth updating the implementation once SymbolKit adopts
// Swift 5.7 as its minimum language requirement.


// When working with `Mixin` values in a generic (non-specific) context,
// we only know their value conforms to the existential type `Mixin`. This
// extension to `Mixin` and the `hash` property defined in it is essential for
// the whole process to work:
// The `hash` property does not expose the `Self` type in its interface and
// therefore is accessible from the existential type `Mixin`. Inside `hash`,
// however, we have access to the concrete type `Self`, allowing us to initialize
// the `HashableDetector` with a concrete generic type, which can know it conforms
// to `Hashable`. If we were to simply pass a value of type `Any` into the initializer
// of `HashableDetector`, the latter would not recognize `value` as `Hashable`, even
// if the original concrete type were to conform to `Hashable`.
extension Mixin {
    /// This ``Mixin``s `hash(into:)` function, available
    /// only if this ``Mixin`` conforms to `Hashable`.
    var hash: ((inout Hasher) -> Void)? {
        (HashableDetector(value: self) as? AnyHashable)?.hash
    }
}

// The `AnyEquatable` protocol simply defines our requirement for a hash
// function. It has no Self or associated type requirements and thus can
// be casted to via a simple `as?`. In Swift 5.7 we could simply cast to
// `any Hashable`, but this was not possible before.
private protocol AnyHashable {
    var hash: (inout Hasher) -> Void { get }
}

// The `HashableDetector` brings both pieces together by conditionally conforming
// itself to `AnyHashable` where its generic `value` is `Hashable`.
private struct HashableDetector<T> {
    let value: T
}

extension HashableDetector: AnyHashable where T: Hashable {
    var hash: (inout Hasher) -> Void {
        value.hash(into:)
    }
}