Skip to content

Commit a8f2e1f

Browse files
authored
Make hashcode of enum items stable (#23218)
Fixes #19177.
2 parents 43c6ee7 + 5c2e39e commit a8f2e1f

File tree

3 files changed

+44
-8
lines changed

3 files changed

+44
-8
lines changed

compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
6969
myCaseSymbols = defn.caseClassSynthesized
7070
myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals)
7171
myEnumValueSymbols = List(defn.Product_productPrefix)
72-
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal
72+
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal :+ defn.Any_hashCode
7373
}
7474

7575
def valueSymbols(using Context): List[Symbol] = { initSymbols; myValueSymbols }
@@ -117,6 +117,12 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
117117
def syntheticDefIfMissing(sym: Symbol): List[Tree] =
118118
if (existingDef(sym, clazz).exists) Nil else syntheticDef(sym) :: Nil
119119

120+
def identifierRef: Tree =
121+
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
122+
ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get)
123+
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
124+
Literal(Constant(clazz.owner.name.toString))
125+
120126
def syntheticDef(sym: Symbol): Tree = {
121127
val synthetic = sym.copy(
122128
owner = clazz,
@@ -136,12 +142,6 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
136142
else
137143
identifierRef
138144

139-
def identifierRef: Tree =
140-
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
141-
ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get)
142-
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
143-
Literal(Constant(clazz.owner.name.toString))
144-
145145
def ordinalRef: Tree =
146146
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
147147
ref(clazz.owner.paramSymss.head.find(_.name == nme.ordinalDollar_).get)
@@ -358,7 +358,8 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
358358
* For case classes with primitive paramters, see [[caseHashCodeBody]].
359359
*/
360360
def chooseHashcode(using Context) =
361-
if (accessors.isEmpty) Literal(Constant(ownName.hashCode))
361+
if (isNonJavaEnumValue) identifierRef.select(nme.hashCode_).appliedToTermArgs(Nil)
362+
else if (accessors.isEmpty) Literal(Constant(ownName.hashCode))
362363
else if (accessors.exists(_.info.finalResultType.classSymbol.isPrimitiveValueClass))
363364
caseHashCodeBody
364365
else

tests/run/stable-enum-hashcodes.check

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
65
2+
65
3+
66
4+
66
5+
67
6+
67
7+
68
8+
68
9+
-1449359058
10+
-1449359058
11+
194551161
12+
194551161

tests/run/stable-enum-hashcodes.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
enum Enum:
2+
case A
3+
case B
4+
case C()
5+
case D()
6+
case E(x: Int)
7+
8+
@main def Test =
9+
// Enum values (were not stable from run to run before #23218)
10+
println(Enum.A.hashCode)
11+
println(Enum.A.hashCode)
12+
println(Enum.B.hashCode)
13+
println(Enum.B.hashCode)
14+
15+
// Other enum cases (were already stable from run to run)
16+
println(Enum.C().hashCode)
17+
println(Enum.C().hashCode)
18+
println(Enum.D().hashCode)
19+
println(Enum.D().hashCode)
20+
println(Enum.E(1).hashCode)
21+
println(Enum.E(1).hashCode)
22+
println(Enum.E(2).hashCode)
23+
println(Enum.E(2).hashCode)

0 commit comments

Comments
 (0)