Skip to content

Commit 110ee05

Browse files
committed
[SE-0206][test] Update diagnostic tests for Hashable derivation
1 parent fa26420 commit 110ee05

File tree

2 files changed

+82
-12
lines changed

2 files changed

+82
-12
lines changed

test/Sema/enum_conformance_synthesis.swift

+13-2
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// RUN: cp %s %t/main.swift
33
// RUN: %target-swift-frontend -typecheck -verify -primary-file %t/main.swift %S/Inputs/enum_conformance_synthesis_other.swift -verify-ignore-unknown
44

5+
var hasher = Hasher()
6+
57
enum Foo: CaseIterable {
68
case A, B
79
}
810

911
if Foo.A == .B { }
1012
var aHash: Int = Foo.A.hashValue
13+
Foo.A.hash(into: &hasher)
1114
_ = Foo.allCases
1215

1316
Foo.A == Foo.B // expected-warning {{result of operator '==' is unused}}
@@ -24,6 +27,7 @@ enum Generic<T>: CaseIterable {
2427

2528
if Generic<Foo>.A == .B { }
2629
var gaHash: Int = Generic<Foo>.A.hashValue
30+
Generic<Foo>.A.hash(into: &hasher)
2731
_ = Generic<Foo>.allCases
2832

2933
func localEnum() -> Bool {
@@ -45,6 +49,7 @@ func ==(x: CustomHashable, y: CustomHashable) -> Bool { // expected-note 4 {{non
4549

4650
if CustomHashable.A == .B { }
4751
var custHash: Int = CustomHashable.A.hashValue
52+
CustomHashable.A.hash(into: &hasher)
4853

4954
// We still synthesize conforming overloads of '==' and 'hashValue' if
5055
// explicit definitions don't satisfy the protocol requirements. Probably
@@ -61,6 +66,7 @@ if InvalidCustomHashable.A == .B { }
6166
var s: String = InvalidCustomHashable.A == .B
6267
s = InvalidCustomHashable.A.hashValue
6368
var i: Int = InvalidCustomHashable.A.hashValue
69+
InvalidCustomHashable.A.hash(into: &hasher)
6470

6571
// Check use of an enum's synthesized members before the enum is actually declared.
6672
struct UseEnumBeforeDeclaration {
@@ -113,6 +119,10 @@ _ = EnumWithHashablePayload.A(1).hashValue
113119
_ = EnumWithHashablePayload.B("x", 1).hashValue
114120
_ = EnumWithHashablePayload.C.hashValue
115121

122+
EnumWithHashablePayload.A(1).hash(into: &hasher)
123+
EnumWithHashablePayload.B("x", 1).hash(into: &hasher)
124+
EnumWithHashablePayload.C.hash(into: &hasher)
125+
116126
// ...and they should also inherit equatability from Hashable.
117127
if EnumWithHashablePayload.A(1) == .B("x", 1) { }
118128
if EnumWithHashablePayload.A(1) == .C { }
@@ -135,12 +145,13 @@ var genericHashableHash: Int = GenericHashable<String>.A("a").hashValue
135145

136146
// But it should be an error if the generic argument doesn't have the necessary
137147
// constraints to satisfy the conditions for derivation.
138-
enum GenericNotHashable<T: Equatable>: Hashable { // expected-error {{does not conform}}
148+
enum GenericNotHashable<T: Equatable>: Hashable { // expected-error 2 {{does not conform to protocol 'Hashable'}}
139149
case A(T)
140150
case B
141151
}
142152
if GenericNotHashable<String>.A("a") == .B { }
143-
var genericNotHashableHash: Int = GenericNotHashable<String>.A("a").hashValue // expected-error {{value of type 'GenericNotHashable<String>' has no member 'hashValue'}}
153+
let _: Int = GenericNotHashable<String>.A("a").hashValue // No error. hashValue is always synthesized, even if Hashable derivation fails
154+
GenericNotHashable<String>.A("a").hash(into: &hasher) // expected-error {{value of type 'GenericNotHashable<String>' has no member 'hash'}}
144155

145156
// An enum with no cases should not derive conformance.
146157
enum NoCases: Hashable {} // expected-error 2 {{does not conform}}

test/Sema/struct_equatable_hashable.swift

+69-10
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
// RUN: cp %s %t/main.swift
33
// RUN: %target-swift-frontend -typecheck -verify -primary-file %t/main.swift %S/Inputs/struct_equatable_hashable_other.swift -verify-ignore-unknown
44

5+
var hasher = Hasher()
6+
57
struct Point: Hashable {
68
let x: Int
79
let y: Int
810
}
911

1012
if Point(x: 1, y: 2) == Point(x: 2, y: 1) { }
11-
var pointHash: Int = Point(x: 3, y: 5).hashValue
13+
let pointHash: Int = Point(x: 3, y: 5).hashValue
14+
Point(x: 3, y: 5).hash(into: &hasher)
1215

1316
Point(x: 1, y: 2) == Point(x: 2, y: 1) // expected-warning {{result of operator '==' is unused}}
1417

@@ -24,7 +27,8 @@ struct Pair<T: Hashable>: Hashable {
2427
let p1 = Pair(first: "a", second: "b")
2528
let p2 = Pair(first: "a", second: "c")
2629
if p1 == p2 { }
27-
var pairHash: Int = p1.hashValue
30+
let _: Int = p1.hashValue
31+
p1.hash(into: &hasher)
2832

2933
func localStruct() -> Bool {
3034
struct Local: Equatable {
@@ -34,22 +38,49 @@ func localStruct() -> Bool {
3438
return Local(v: 5) == Local(v: 4)
3539
}
3640

37-
struct CustomHashable: Hashable {
41+
42+
//------------------------------------------------------------------------------
43+
// Verify compiler can derive hash(into:) implementation from hashValue
44+
45+
struct CustomHashValue: Hashable {
3846
let x: Int
3947
let y: Int
4048

4149
var hashValue: Int { return 0 }
4250

43-
static func ==(x: CustomHashable, y: CustomHashable) -> Bool { return true } // expected-note 2 {{non-matching type}}
51+
static func ==(x: CustomHashValue, y: CustomHashValue) -> Bool { return true } // expected-note 2 {{non-matching type}}
52+
}
53+
54+
if CustomHashValue(x: 1, y: 2) == CustomHashValue(x: 2, y: 3) { }
55+
let _: Int = CustomHashValue(x: 1, y: 2).hashValue
56+
CustomHashValue(x: 1, y: 2).hash(into: &hasher)
57+
58+
59+
//------------------------------------------------------------------------------
60+
// Verify compiler can derive hashValue implementation from hash(into:)
61+
62+
struct CustomHashInto: Hashable {
63+
let x: Int
64+
let y: Int
65+
66+
func hash(into hasher: inout Hasher) {
67+
hasher.combine(x)
68+
hasher.combine(y)
69+
}
70+
71+
static func ==(x: CustomHashInto, y: CustomHashInto) -> Bool { return true } // expected-note 2 {{non-matching type}}
4472
}
4573

46-
if CustomHashable(x: 1, y: 2) == CustomHashable(x: 2, y: 3) { }
47-
var custHash: Int = CustomHashable(x: 1, y: 2).hashValue
74+
if CustomHashInto(x: 1, y: 2) == CustomHashInto(x: 2, y: 3) { }
75+
let _: Int = CustomHashInto(x: 1, y: 2).hashValue
76+
CustomHashInto(x: 1, y: 2).hash(into: &hasher)
77+
4878

4979
// Check use of an struct's synthesized members before the struct is actually declared.
5080
struct UseStructBeforeDeclaration {
5181
let eqValue = StructToUseBeforeDeclaration(v: 4) == StructToUseBeforeDeclaration(v: 5)
5282
let hashValue = StructToUseBeforeDeclaration(v: 1).hashValue
83+
let hashInto: (inout Hasher) -> Void = StructToUseBeforeDeclaration(v: 1).hash(into:)
5384
}
5485
struct StructToUseBeforeDeclaration: Hashable {
5586
let v: Int
@@ -58,6 +89,7 @@ struct StructToUseBeforeDeclaration: Hashable {
5889
// Check structs from another file in the same module.
5990
if FromOtherFile(v: "a") == FromOtherFile(v: "b") {}
6091
let _: Int = FromOtherFile(v: "c").hashValue
92+
FromOtherFile(v: "d").hash(into: &hasher)
6193

6294
func getFromOtherFile() -> AlsoFromOtherFile { return AlsoFromOtherFile(v: 4) }
6395
if AlsoFromOtherFile(v: 3) == getFromOtherFile() {}
@@ -93,23 +125,26 @@ struct StructIgnoresComputedProperties: Hashable {
93125
}
94126
if StructIgnoresComputedProperties(a: 1, b: "a") == StructIgnoresComputedProperties(a: 2, b: "c") {}
95127
let _: Int = StructIgnoresComputedProperties(a: 3, b: "p").hashValue
96-
128+
StructIgnoresComputedProperties(a: 4, b: "q").hash(into: &hasher)
97129

98130
// Structs should be able to derive conformances based on the conformances of
99131
// their generic arguments.
100132
struct GenericHashable<T: Hashable>: Hashable {
101133
let value: T
102134
}
103135
if GenericHashable<String>(value: "a") == GenericHashable<String>(value: "b") { }
104-
var genericHashableHash: Int = GenericHashable<String>(value: "a").hashValue
136+
let _: Int = GenericHashable<String>(value: "c").hashValue
137+
GenericHashable<String>(value: "c").hash(into: &hasher)
105138

106139
// But it should be an error if the generic argument doesn't have the necessary
107140
// constraints to satisfy the conditions for derivation.
108-
struct GenericNotHashable<T: Equatable>: Hashable { // expected-error {{does not conform}}
141+
struct GenericNotHashable<T: Equatable>: Hashable { // expected-error 2 {{does not conform to protocol 'Hashable'}}
109142
let value: T
110143
}
111144
if GenericNotHashable<String>(value: "a") == GenericNotHashable<String>(value: "b") { }
112-
var genericNotHashableHash: Int = GenericNotHashable<String>(value: "a").hashValue // expected-error {{value of type 'GenericNotHashable<String>' has no member 'hashValue'}}
145+
let gnh = GenericNotHashable<String>(value: "b")
146+
let _: Int = gnh.hashValue // No error. hashValue is always synthesized, even if Hashable derivation fails
147+
gnh.hash(into: &hasher) // expected-error {{value of type 'GenericNotHashable<String>' has no member 'hash'}}
113148

114149

115150
// Conformance cannot be synthesized in an extension.
@@ -148,6 +183,30 @@ extension OtherFileNonconforming: Hashable {
148183
// ...but synthesis in a type defined in another file doesn't work yet.
149184
extension YetOtherFileNonconforming: Equatable {} // expected-error {{cannot be automatically synthesized in an extension}}
150185

186+
// Verify that we can add Hashable conformance in an extension by only
187+
// implementing hash(into:)
188+
struct StructConformsAndImplementsHashIntoInExtension: Equatable {
189+
let v: String
190+
}
191+
extension StructConformsAndImplementsHashIntoInExtension: Hashable {
192+
func hash(into hasher: inout Hasher) {
193+
hasher.combine(v)
194+
}
195+
}
196+
let _: Int = StructConformsAndImplementsHashIntoInExtension(v: "a").hashValue
197+
StructConformsAndImplementsHashIntoInExtension(v: "b").hash(into: &hasher)
198+
199+
struct GenericHashIntoInExtension<T: Hashable>: Equatable {
200+
let value: T
201+
}
202+
extension GenericHashIntoInExtension: Hashable {
203+
func hash(into hasher: inout Hasher) {
204+
hasher.combine(value)
205+
}
206+
}
207+
let _: Int = GenericHashIntoInExtension<String>(value: "a").hashValue
208+
GenericHashIntoInExtension(value: "b").hash(into: &hasher)
209+
151210
// FIXME: Remove -verify-ignore-unknown.
152211
// <unknown>:0: error: unexpected error produced: invalid redeclaration of 'hashValue'
153212
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool'

0 commit comments

Comments
 (0)