Skip to content

Add default Hashable implementations for RawRepresentable types #20705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions stdlib/public/core/CompilerProtocols.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,27 @@ public func != <T : Equatable>(lhs: T, rhs: T) -> Bool
return lhs.rawValue != rhs.rawValue
}

// Ensure that any RawRepresentable types that conform to Hashable without
// providing explicit implementations get hashing that's consistent with the ==
// definition above. (Compiler-synthesized hashing is based on stored properties
// rather than rawValue; the difference is subtle, but it can be fatal.)
extension RawRepresentable where RawValue: Hashable, Self: Hashable {
@inlinable // trivial
public var hashValue: Int {
return rawValue.hashValue
}

@inlinable // trivial
public func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}

@inlinable // trivial
public func _rawHashValue(seed: Int) -> Int {
return rawValue._rawHashValue(seed: seed)
}
}

/// A type that provides a collection of all of its values.
///
/// Types that conform to the `CaseIterable` protocol are typically
Expand Down
13 changes: 13 additions & 0 deletions stdlib/public/core/FloatingPoint.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,19 @@ public enum FloatingPointSign: Int {
public static func ==(a: FloatingPointSign, b: FloatingPointSign) -> Bool {
return a.rawValue == b.rawValue
}

@inlinable
public var hashValue: Int { return rawValue.hashValue }

@inlinable
public func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}

@inlinable
public func _rawHashValue(seed: Int) -> Int {
return rawValue._rawHashValue(seed: seed)
}
}

/// The IEEE 754 floating-point classes.
Expand Down
15 changes: 15 additions & 0 deletions stdlib/public/core/UnicodeScalarProperties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1258,12 +1258,27 @@ extension Unicode {
self.rawValue = rawValue
}

public static func == (
lhs: CanonicalCombiningClass,
rhs: CanonicalCombiningClass
) -> Bool {
return lhs.rawValue == rhs.rawValue
}

public static func < (
lhs: CanonicalCombiningClass,
rhs: CanonicalCombiningClass
) -> Bool {
return lhs.rawValue < rhs.rawValue
}

public var hashValue: Int {
return rawValue.hashValue
}

public func hash(into hasher: inout Hasher) {
hasher.combine(rawValue)
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions test/IDE/complete_enum_elements.swift
Original file line number Diff line number Diff line change
Expand Up @@ -267,8 +267,8 @@ enum QuxEnum : Int {
// QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux1[#QuxEnum#]{{; name=.+$}}
// QUX_ENUM_NO_DOT-NEXT: Decl[EnumElement]/CurrNominal: .Qux2[#QuxEnum#]{{; name=.+$}}
// QUX_ENUM_NO_DOT-NEXT: Decl[TypeAlias]/CurrNominal: .RawValue[#Int#]{{; name=.+$}}
// QUX_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/CurrNominal: .hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
// QUX_ENUM_NO_DOT-NEXT: Decl[Constructor]/CurrNominal: ({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}}
// QUX_ENUM_NO_DOT-NEXT: Decl[InstanceMethod]/Super: .hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
// QUX_ENUM_NO_DOT-NEXT: Keyword[self]/CurrNominal: .self[#QuxEnum.Type#]; name=self
// QUX_ENUM_NO_DOT-NEXT: End completions

Expand All @@ -277,8 +277,8 @@ enum QuxEnum : Int {
// QUX_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Qux1[#QuxEnum#]{{; name=.+$}}
// QUX_ENUM_DOT-NEXT: Decl[EnumElement]/CurrNominal: Qux2[#QuxEnum#]{{; name=.+$}}
// QUX_ENUM_DOT-NEXT: Decl[TypeAlias]/CurrNominal: RawValue[#Int#]{{; name=.+$}}
// QUX_ENUM_DOT-NEXT: Decl[InstanceMethod]/CurrNominal/NotRecommended/TypeRelation[Invalid]: hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
// QUX_ENUM_DOT-NEXT: Decl[Constructor]/CurrNominal: init({#rawValue: Int#})[#QuxEnum?#]{{; name=.+$}}
// QUX_ENUM_DOT-NEXT: Decl[InstanceMethod]/Super/NotRecommended/TypeRelation[Invalid]: hash({#self: QuxEnum#})[#(into: inout Hasher) -> Void#]; name=hash(QuxEnum)
// QUX_ENUM_DOT-NEXT: End completions

func freeFunc() {}
Expand Down
4 changes: 0 additions & 4 deletions test/IDE/print_ast_tc_decls.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1073,8 +1073,6 @@ enum d2300_EnumDeclWithValues1 : Int {
// PASS_COMMON-NEXT: {{^}} case EDV2_First{{$}}
// PASS_COMMON-NEXT: {{^}} case EDV2_Second{{$}}
// PASS_COMMON-NEXT: {{^}} typealias RawValue = Int
// PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}}
// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher)
// PASS_COMMON-NEXT: {{^}} init?(rawValue: Int){{$}}
// PASS_COMMON-NEXT: {{^}} var rawValue: Int { get }{{$}}
// PASS_COMMON-NEXT: {{^}}}{{$}}
Expand All @@ -1087,8 +1085,6 @@ enum d2400_EnumDeclWithValues2 : Double {
// PASS_COMMON-NEXT: {{^}} case EDV3_First{{$}}
// PASS_COMMON-NEXT: {{^}} case EDV3_Second{{$}}
// PASS_COMMON-NEXT: {{^}} typealias RawValue = Double
// PASS_COMMON-NEXT: {{^}} var hashValue: Int { get }{{$}}
// PASS_COMMON-NEXT: {{^}} func hash(into hasher: inout Hasher)
// PASS_COMMON-NEXT: {{^}} init?(rawValue: Double){{$}}
// PASS_COMMON-NEXT: {{^}} var rawValue: Double { get }{{$}}
// PASS_COMMON-NEXT: {{^}}}{{$}}
Expand Down
8 changes: 0 additions & 8 deletions test/ParseableInterface/synthesized.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ public enum HasRawValue: Int {
// CHECK-NEXT: case a, b, c
case a, b = 5, c
// CHECK-NEXT: public typealias RawValue = Swift.Int
// CHECK-NEXT: public var hashValue: Swift.Int {
// CHECK-NEXT: get{{$}}
// CHECK-NEXT: }
// CHECK-NEXT: public func hash(into hasher: inout Swift.Hasher)
// CHECK-NEXT: @inlinable public init?(rawValue: Swift.Int)
// CHECK-NEXT: public var rawValue: Swift.Int {
// CHECK-NEXT: @inlinable get{{$}}
Expand All @@ -20,10 +16,6 @@ public enum HasRawValue: Int {
// CHECK-NEXT: case a, b, c
case a, b = 5, c
// CHECK-NEXT: public typealias RawValue = Swift.Int
// CHECK-NEXT: public var hashValue: Swift.Int {
// CHECK-NEXT: get{{$}}
// CHECK-NEXT: }
// CHECK-NEXT: public func hash(into hasher: inout Swift.Hasher)
// CHECK-NEXT: @inlinable public init?(rawValue: Swift.Int)
// CHECK-NEXT: public var rawValue: Swift.Int {
// CHECK-NEXT: @inlinable get{{$}}
Expand Down
1 change: 0 additions & 1 deletion test/SILGen/objc_enum.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import gizmo

// CHECK-DAG: sil shared [serializable] @$sSo16NSRuncingOptionsV{{[_0-9a-zA-Z]*}}fC
// CHECK-DAG: sil shared [serializable] @$sSo16NSRuncingOptionsV8rawValueSivg
// CHECK-DAG: sil shared [serializable] @$sSo16NSRuncingOptionsV9hashValueSivg

// Non-payload enum ctors don't need to be instantiated at all.
// NEGATIVE-NOT: sil shared [transparent] @$sSo16NSRuncingOptionsV5MinceAbBmF
Expand Down
Loading