Skip to content

Commit e804628

Browse files
committed
[test] Update diagnostic tests for Hashable derivation
# Conflicts: # test/Sema/enum_conformance_synthesis.swift
1 parent f0246b6 commit e804628

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

test/Sema/enum_conformance_synthesis.swift

Lines changed: 13 additions & 2 deletions
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

Lines changed: 41 additions & 7 deletions
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 {
@@ -44,12 +48,14 @@ struct CustomHashable: Hashable {
4448
}
4549

4650
if CustomHashable(x: 1, y: 2) == CustomHashable(x: 2, y: 3) { }
47-
var custHash: Int = CustomHashable(x: 1, y: 2).hashValue
51+
let _: Int = CustomHashable(x: 1, y: 2).hashValue
52+
CustomHashable(x: 1, y: 2)._hash(into: &hasher)
4853

4954
// Check use of an struct's synthesized members before the struct is actually declared.
5055
struct UseStructBeforeDeclaration {
5156
let eqValue = StructToUseBeforeDeclaration(v: 4) == StructToUseBeforeDeclaration(v: 5)
5257
let hashValue = StructToUseBeforeDeclaration(v: 1).hashValue
58+
let hashInto: (inout _Hasher) -> Void = StructToUseBeforeDeclaration(v: 1)._hash(into:)
5359
}
5460
struct StructToUseBeforeDeclaration: Hashable {
5561
let v: Int
@@ -58,6 +64,7 @@ struct StructToUseBeforeDeclaration: Hashable {
5864
// Check structs from another file in the same module.
5965
if FromOtherFile(v: "a") == FromOtherFile(v: "b") {}
6066
let _: Int = FromOtherFile(v: "c").hashValue
67+
FromOtherFile(v: "d")._hash(into: &hasher)
6168

6269
func getFromOtherFile() -> AlsoFromOtherFile { return AlsoFromOtherFile(v: 4) }
6370
if AlsoFromOtherFile(v: 3) == getFromOtherFile() {}
@@ -93,23 +100,26 @@ struct StructIgnoresComputedProperties: Hashable {
93100
}
94101
if StructIgnoresComputedProperties(a: 1, b: "a") == StructIgnoresComputedProperties(a: 2, b: "c") {}
95102
let _: Int = StructIgnoresComputedProperties(a: 3, b: "p").hashValue
96-
103+
StructIgnoresComputedProperties(a: 4, b: "q")._hash(into: &hasher)
97104

98105
// Structs should be able to derive conformances based on the conformances of
99106
// their generic arguments.
100107
struct GenericHashable<T: Hashable>: Hashable {
101108
let value: T
102109
}
103110
if GenericHashable<String>(value: "a") == GenericHashable<String>(value: "b") { }
104-
var genericHashableHash: Int = GenericHashable<String>(value: "a").hashValue
111+
let _: Int = GenericHashable<String>(value: "c").hashValue
112+
GenericHashable<String>(value: "c")._hash(into: &hasher)
105113

106114
// But it should be an error if the generic argument doesn't have the necessary
107115
// constraints to satisfy the conditions for derivation.
108-
struct GenericNotHashable<T: Equatable>: Hashable { // expected-error {{does not conform}}
116+
struct GenericNotHashable<T: Equatable>: Hashable { // expected-error 2 {{does not conform to protocol 'Hashable'}}
109117
let value: T
110118
}
111119
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'}}
120+
let gnh = GenericNotHashable<String>(value: "b")
121+
let _: Int = gnh.hashValue // No error. hashValue is always synthesized, even if Hashable derivation fails
122+
gnh._hash(into: &hasher) // expected-error {{value of type 'GenericNotHashable<String>' has no member '_hash'}}
113123

114124

115125
// Conformance cannot be synthesized in an extension.
@@ -148,6 +158,30 @@ extension OtherFileNonconforming: Hashable {
148158
// ...but synthesis in a type defined in another file doesn't work yet.
149159
extension YetOtherFileNonconforming: Equatable {} // expected-error {{cannot be automatically synthesized in an extension}}
150160

161+
// Verify that we can add Hashable conformance in an extension by only
162+
// implementing _hash(into:)
163+
struct StructConformsAndImplementsHashIntoInExtension: Equatable {
164+
let v: String
165+
}
166+
extension StructConformsAndImplementsHashIntoInExtension: Hashable {
167+
func _hash(into hasher: inout _Hasher) {
168+
hasher.append(v)
169+
}
170+
}
171+
let _: Int = StructConformsAndImplementsHashIntoInExtension(v: "a").hashValue
172+
StructConformsAndImplementsHashIntoInExtension(v: "b")._hash(into: &hasher)
173+
174+
struct GenericHashIntoInExtension<T: Hashable>: Equatable {
175+
let value: T
176+
}
177+
extension GenericHashIntoInExtension: Hashable {
178+
func _hash(into hasher: inout _Hasher) {
179+
hasher.append(value)
180+
}
181+
}
182+
let _: Int = GenericHashIntoInExtension<String>(value: "a").hashValue
183+
GenericHashIntoInExtension(value: "b")._hash(into: &hasher)
184+
151185
// FIXME: Remove -verify-ignore-unknown.
152186
// <unknown>:0: error: unexpected error produced: invalid redeclaration of 'hashValue'
153187
// <unknown>:0: error: unexpected note produced: candidate has non-matching type '(Foo, Foo) -> Bool'

0 commit comments

Comments
 (0)