Skip to content

Commit f055afc

Browse files
authored
Merge pull request #473 from scala/backport-lts-3.3-23218
Backport "Make hashcode of enum items stable" to 3.3 LTS
2 parents 82fdaf5 + 6184594 commit f055afc

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
@@ -68,7 +68,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
6868
myCaseSymbols = defn.caseClassSynthesized
6969
myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals)
7070
myEnumValueSymbols = List(defn.Product_productPrefix)
71-
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal
71+
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal :+ defn.Any_hashCode
7272
}
7373

7474
def valueSymbols(using Context): List[Symbol] = { initSymbols; myValueSymbols }
@@ -110,6 +110,12 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
110110
def syntheticDefIfMissing(sym: Symbol): List[Tree] =
111111
if (existingDef(sym, clazz).exists) Nil else syntheticDef(sym) :: Nil
112112

113+
def identifierRef: Tree =
114+
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
115+
ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get)
116+
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
117+
Literal(Constant(clazz.owner.name.toString))
118+
113119
def syntheticDef(sym: Symbol): Tree = {
114120
val synthetic = sym.copy(
115121
owner = clazz,
@@ -129,12 +135,6 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
129135
else
130136
identifierRef
131137

132-
def identifierRef: Tree =
133-
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
134-
ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get)
135-
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
136-
Literal(Constant(clazz.owner.name.toString))
137-
138138
def ordinalRef: Tree =
139139
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
140140
ref(clazz.owner.paramSymss.head.find(_.name == nme.ordinalDollar_).get)
@@ -322,7 +322,8 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
322322
* For case classes with primitive paramters, see [[caseHashCodeBody]].
323323
*/
324324
def chooseHashcode(using Context) =
325-
if (accessors.isEmpty) Literal(Constant(ownName.hashCode))
325+
if (isNonJavaEnumValue) identifierRef.select(nme.hashCode_).appliedToTermArgs(Nil)
326+
else if (accessors.isEmpty) Literal(Constant(ownName.hashCode))
326327
else if (accessors.exists(_.info.finalResultType.classSymbol.isPrimitiveValueClass))
327328
caseHashCodeBody
328329
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)