Skip to content

Commit d2a160e

Browse files
authored
Merge pull request #9989 from dotty-staging/topic/enum-dropEnumLabel
Drop enumLabel method from enums
2 parents bafb0d8 + 45a8972 commit d2a160e

File tree

12 files changed

+36
-85
lines changed

12 files changed

+36
-85
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,7 @@ object desugar {
594594
def enumCaseMeths =
595595
if isEnumCase then
596596
val (ordinal, scaffolding) = nextOrdinal(className, CaseKind.Class, definesEnumLookupMethods(cdef))
597-
(ordinalMethLit(ordinal) :: enumLabelLit(className.toString) :: Nil, scaffolding)
597+
(ordinalMethLit(ordinal) :: Nil, scaffolding)
598598
else (Nil, Nil)
599599
def copyMeths = {
600600
val hasRepeatedParam = constrVparamss.nestedExists {

compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -175,20 +175,11 @@ object DesugarEnums {
175175
/** A creation method for a value of enum type `E`, which is defined as follows:
176176
*
177177
* private def $new(_$ordinal: Int, $name: String) = new E with scala.runtime.EnumValue {
178-
* def ordinal = _$ordinal // if `E` does not derive from `java.lang.Enum`
179-
* def enumLabel = $name // if `E` does not derive from `java.lang.Enum`
180-
* def enumLabel = this.name // if `E` derives from `java.lang.Enum`
178+
* def ordinal = _$ordinal // if `E` does not derive from `java.lang.Enum`
181179
* }
182180
*/
183181
private def enumValueCreator(using Context) = {
184-
val fieldMethods =
185-
if isJavaEnum then
186-
val enumLabelDef = enumLabelMeth(Select(This(Ident(tpnme.EMPTY)), nme.name))
187-
enumLabelDef :: Nil
188-
else
189-
val ordinalDef = ordinalMeth(Ident(nme.ordinalDollar_))
190-
val enumLabelDef = enumLabelMeth(Ident(nme.nameDollar))
191-
ordinalDef :: enumLabelDef :: Nil
182+
val fieldMethods = if isJavaEnum then Nil else ordinalMeth(Ident(nme.ordinalDollar_)) :: Nil
192183
val creator = New(Template(
193184
constr = emptyConstructor,
194185
parents = enumClassRef :: scalaRuntimeDot(tpnme.EnumValue) :: Nil,
@@ -284,15 +275,9 @@ object DesugarEnums {
284275
def ordinalMeth(body: Tree)(using Context): DefDef =
285276
DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withAddedFlags(Synthetic)
286277

287-
def enumLabelMeth(body: Tree)(using Context): DefDef =
288-
DefDef(nme.enumLabel, Nil, Nil, TypeTree(defn.StringType), body).withAddedFlags(Synthetic)
289-
290278
def ordinalMethLit(ord: Int)(using Context): DefDef =
291279
ordinalMeth(Literal(Constant(ord)))
292280

293-
def enumLabelLit(name: String)(using Context): DefDef =
294-
enumLabelMeth(Literal(Constant(name)))
295-
296281
/** Expand a module definition representing a parameterless enum case */
297282
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, definesLookups: Boolean, span: Span)(using Context): Tree = {
298283
assert(impl.body.isEmpty)
@@ -301,11 +286,9 @@ object DesugarEnums {
301286
expandSimpleEnumCase(name, mods, definesLookups, span)
302287
else {
303288
val (tag, scaffolding) = nextOrdinal(name, CaseKind.Object, definesLookups)
304-
val ordinalDef = if isJavaEnum then Nil else ordinalMethLit(tag) :: Nil
305-
val enumLabelDef = enumLabelLit(name.toString)
306289
val impl1 = cpy.Template(impl)(
307290
parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue),
308-
body = ordinalDef ::: enumLabelDef :: Nil
291+
body = if isJavaEnum then Nil else ordinalMethLit(tag) :: Nil
309292
).withAttachment(ExtendsSingletonMirror, ())
310293
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods.withAddedFlags(EnumValue, span))
311294
flatTree(vdef :: scaffolding).withSpan(span)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,6 @@ object StdNames {
456456
val emptyValDef: N = "emptyValDef"
457457
val end: N = "end"
458458
val ensureAccessible : N = "ensureAccessible"
459-
val enumLabel: N = "enumLabel"
460459
val eq: N = "eq"
461460
val eqInstance: N = "eqInstance"
462461
val equalsNumChar : N = "equalsNumChar"

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
9595
if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased`
9696
else clazz.caseAccessors
9797
val isEnumValue = clazz.isAnonymousClass && clazz.classParents.head.classSymbol.is(Enum)
98-
val isNonJavaEnumValue = isEnumValue && !clazz.derivesFrom(defn.JavaEnumClass)
98+
val isSimpleEnumValue = isEnumValue && !clazz.owner.isAllOf(EnumCase)
99+
val isJavaEnumValue = isEnumValue && clazz.derivesFrom(defn.JavaEnumClass)
100+
val isNonJavaEnumValue = isEnumValue && !isJavaEnumValue
99101

100102
val symbolsToSynthesize: List[Symbol] =
101103
if (clazz.is(Case))
@@ -122,12 +124,21 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
122124
def ownName: Tree =
123125
Literal(Constant(clazz.name.stripModuleClassSuffix.toString))
124126

125-
def callEnumLabel: Tree =
126-
Select(This(clazz), nme.enumLabel).ensureApplied
127+
def nameRef: Tree =
128+
if isJavaEnumValue then
129+
Select(This(clazz), nme.name).ensureApplied
130+
else
131+
identifierRef
132+
133+
def identifierRef: Tree =
134+
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
135+
ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get)
136+
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
137+
Literal(Constant(clazz.owner.name.toString))
127138

128139
def toStringBody(vrefss: List[List[Tree]]): Tree =
129140
if (clazz.is(ModuleClass)) ownName
130-
else if (isNonJavaEnumValue) callEnumLabel
141+
else if (isNonJavaEnumValue) identifierRef
131142
else forwardToRuntime(vrefss.head)
132143

133144
def syntheticRHS(vrefss: List[List[Tree]])(using Context): Tree = synthetic.name match {
@@ -137,7 +148,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
137148
case nme.equals_ => equalsBody(vrefss.head.head)
138149
case nme.canEqual_ => canEqualBody(vrefss.head.head)
139150
case nme.productArity => Literal(Constant(accessors.length))
140-
case nme.productPrefix if isEnumValue => callEnumLabel
151+
case nme.productPrefix if isEnumValue => nameRef
141152
case nme.productPrefix => ownName
142153
case nme.productElement => productElementBody(accessors.length, vrefss.head.head)
143154
case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head)

docs/docs/reference/enums/desugarEnums.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ map into `case class`es or `val`s.
3434
```scala
3535
enum E ... { <defs> <cases> }
3636
```
37-
expands to a `sealed abstract` class that extends the `scala.Enum` trait and
37+
expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and
3838
an associated companion object that contains the defined cases, expanded according
3939
to rules (2 - 8). The enum class starts with a compiler-generated import that imports
4040
the names `<caseIds>` of all cases so that they can be used without prefix in the class.
4141
```scala
42-
sealed abstract class E ... extends <parents> with scala.Enum {
42+
sealed abstract class E ... extends <parents> with scala.reflect.Enum {
4343
import E.{ <caseIds> }
4444
<defs>
4545
}
@@ -165,7 +165,7 @@ An enum `E` (possibly generic) that defines one or more singleton cases
165165
will define the following additional synthetic members in its companion object (where `E'` denotes `E` with
166166
any type parameters replaced by wildcards):
167167

168-
- A method `valueOf(name: String): E'`. It returns the singleton case value whose `enumLabel` is `name`.
168+
- A method `valueOf(name: String): E'`. It returns the singleton case value whose identifier is `name`.
169169
- A method `values` which returns an `Array[E']` of all singleton case
170170
values defined by `E`, in the order of their definitions.
171171

@@ -177,17 +177,16 @@ If `E` contains at least one simple case, its companion object will define in ad
177177
```scala
178178
private def $new(_$ordinal: Int, $name: String) = new E with runtime.EnumValue {
179179
def ordinal = _$ordinal
180-
def enumLabel = $name
181-
override def productPrefix = enumLabel // if not overridden in `E`
182-
override def toString = enumLabel // if not overridden in `E`
180+
override def productPrefix = $name // if not overridden in `E`
181+
override def toString = $name // if not overridden in `E`
183182
}
184183
```
185184

186185
The anonymous class also implements the abstract `Product` methods that it inherits from `Enum`.
187186
The `ordinal` method is only generated if the enum does not extend from `java.lang.Enum` (as Scala enums do not extend
188187
`java.lang.Enum`s unless explicitly specified). In case it does, there is no need to generate `ordinal` as
189188
`java.lang.Enum` defines it. Similarly there is no need to override `toString` as that is defined in terms of `name` in
190-
`java.lang.Enum`. Finally, `enumLabel` will call `this.name` when `E` extends `java.lang.Enum`.
189+
`java.lang.Enum`. Finally, `productPrefix` will call `this.name` when `E` extends `java.lang.Enum`.
191190

192191
### Scopes for Enum Cases
193192

docs/docs/reference/enums/enums.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,14 @@ For a more in-depth example of using Scala 3 enums from Java, see [this test](ht
110110
### Implementation
111111

112112
Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait.
113-
This trait defines two public methods, `ordinal` and `enumLabel`:
113+
This trait defines a single public method, `ordinal`:
114114

115115
```scala
116116
package scala.reflect
117117

118118
/** A base trait of all Scala enum definitions */
119119
super trait Enum extends Any with Product with Serializable {
120120

121-
/** A string uniquely identifying a case of an enum */
122-
def enumLabel: String
123-
124121
/** A number uniquely identifying a case of an enum */
125122
def ordinal: Int
126123
}
@@ -133,9 +130,8 @@ For instance, the `Venus` value above would be defined like this:
133130
val Venus: Planet =
134131
new Planet(4.869E24, 6051800.0) {
135132
def ordinal: Int = 1
136-
def enumLabel: String = "Venus"
137-
override def productPrefix: String = enumLabel
138-
override def toString: String = enumLabel
133+
override def productPrefix: String = "Venus"
134+
override def toString: String = "Venus"
139135
}
140136
```
141137

library/src-bootstrapped/scala/reflect/Enum.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,5 @@ package scala.reflect
33
/** A base trait of all Scala enum definitions */
44
super trait Enum extends Any, Product, Serializable:
55

6-
/** A string uniquely identifying a case of an enum */
7-
def enumLabel: String
8-
96
/** A number uniquely identifying a case of an enum */
107
def ordinal: Int

tests/neg/enumsLabel-overrides.scala

Lines changed: 0 additions & 15 deletions
This file was deleted.

tests/neg/enumsLabel-singleimpl.scala

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
enum Labelled {
2-
3-
case A // error: method enumLabel of type => String needs `override` modifier
4-
5-
def enumLabel: String = "nolabel"
6-
7-
}
8-
91
enum Ordinalled {
102

113
case A // error: method ordinal of type => Int needs `override` modifier
124

135
def ordinal: Int = -1
146

157
}
8+
9+
trait HasOrdinal { def ordinal: Int = 23 }
10+
11+
enum MyEnum extends HasOrdinal {
12+
case Foo // error: method ordinal of type => Int needs `override` modifier
13+
}

tests/pos/enum-List-control.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ abstract sealed class List[T] extends reflect.Enum
22
object List {
33
final class Cons[T](x: T, xs: List[T]) extends List[T] {
44
def ordinal = 0
5-
def enumLabel = "Cons"
65
def canEqual(that: Any): Boolean = that.isInstanceOf[Cons[_]]
76
def productArity: Int = 2
87
def productElement(n: Int): Any = n match
@@ -14,7 +13,6 @@ object List {
1413
}
1514
final class Nil[T]() extends List[T], runtime.EnumValue {
1615
def ordinal = 1
17-
def enumLabel = "Nil"
1816
}
1917
object Nil {
2018
def apply[T](): List[T] = new Nil()

tests/run/enum-custom-toString.scala

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,39 @@ abstract class Tag[T] extends Enum
4040
object Tag:
4141
private final class IntTagImpl extends Tag[Int] with runtime.EnumValue:
4242
def ordinal = 0
43-
def enumLabel = "IntTag"
4443
override def hashCode = 123
4544
final val IntTag: Tag[Int] = IntTagImpl()
4645

4746
@main def Test =
4847
assert(ES.A.toString == "overridden", s"ES.A.toString = ${ES.A.toString}")
4948
assert(ES.A.productPrefix == "A", s"ES.A.productPrefix = ${ES.A.productPrefix}")
50-
assert(ES.A.enumLabel == "A", s"ES.A.enumLabel = ${ES.A.enumLabel}")
5149
assert(ES.valueOf("A") == ES.A, s"ES.valueOf(A) = ${ES.valueOf("A")}")
5250
assert(EJ.B.toString == "overridden", s"EJ.B.toString = ${EJ.B.toString}")
5351
assert(EJ.B.productPrefix == "B", s"EJ.B.productPrefix = ${EJ.B.productPrefix}")
54-
assert(EJ.B.enumLabel == "B", s"EJ.B.enumLabel = ${EJ.B.enumLabel}")
5552
assert(EJ.valueOf("B") == EJ.B, s"EJ.valueOf(B) = ${EJ.valueOf("B")}")
5653
assert(EM.C.toString == "overridden", s"EM.C.toString = ${EM.C.toString}")
5754
assert(EM.C.productPrefix == "noprefix", s"EM.C.productPrefix = ${EM.C.productPrefix}")
58-
assert(EM.C.enumLabel == "C", s"EM.C.enumLabel = ${EM.C.enumLabel}")
5955
assert(EM.valueOf("C") == EM.C, s"EM.valueOf(C) = ${EM.valueOf("C")}")
6056
assert(ET.D.toString == "overridden", s"ET.D.toString = ${ET.D.toString}")
6157
assert(ET.D.productPrefix == "D", s"ET.D.productPrefix = ${ET.D.productPrefix}")
62-
assert(ET.D.enumLabel == "D", s"ET.D.enumLabel = ${ET.D.enumLabel}")
6358
assert(EZ.E(0).toString == "overridden", s"EZ.E(0).toString = ${EZ.E(0).toString}")
6459
assert(EZ.E(0).productPrefix == "E", s"EZ.E(0).productPrefix = ${EZ.E(0).productPrefix}")
65-
assert(EZ.E(0).enumLabel == "E", s"EZ.E(0).enumLabel = ${EZ.E(0).enumLabel}")
6660
assert(EC.F.toString == "F", s"EC.F.toString = ${EC.F.toString}")
6761
assert(EC.F.productPrefix == "F", s"EC.F.productPrefix = ${EC.F.productPrefix}")
68-
assert(EC.F.enumLabel == "F", s"EC.F.enumLabel = ${EC.F.enumLabel}")
6962
assert(EC.valueOf("F") == EC.F, s"EC.valueOf(F) = ${EC.valueOf("F")}")
7063
assert(EC.G(0).toString == "G(0)", s"EC.G(0).toString = ${EC.G(0).toString}")
7164
assert(EC.G(0).productPrefix == "G", s"EC.G(0).productPrefix = ${EC.G(0).productPrefix}")
72-
assert(EC.G(0).enumLabel == "G", s"EC.G(0).enumLabel = ${EC.G(0).enumLabel}")
7365
assert(EO.H.toString == "overridden", s"EO.H.toString = ${EO.H.toString}")
7466
assert(EO.H.productPrefix == "noprefix", s"EO.H.productPrefix = ${EO.H.productPrefix}")
75-
assert(EO.H.enumLabel == "H", s"EO.H.enumLabel = ${EO.H.enumLabel}")
7667
assert(EO.valueOf("H") == EO.H, s"EO.valueOf(H) = ${EO.valueOf("H")}")
7768
assert(EO.I(0).toString == "overridden", s"EO.I(0).toString = ${EO.I(0).toString}")
7869
assert(EO.I(0).productPrefix == "noprefix", s"EO.I(0).productPrefix = ${EO.I(0).productPrefix}")
79-
assert(EO.I(0).enumLabel == "I", s"EO.I(0).enumLabel = ${EO.I(0).enumLabel}")
8070
assert(EQ.J.toString == "overridden", s"EQ.J.toString = ${EQ.J.toString}")
8171
assert(EQ.J.productPrefix == "noprefix", s"EQ.J.productPrefix = ${EQ.J.productPrefix}")
82-
assert(EQ.J.enumLabel == "J", s"EQ.J.enumLabel = ${EQ.J.enumLabel}")
8372
assert(EQ.valueOf("J") == EQ.J, s"EQ.valueOf(J) = ${EQ.valueOf("J")}")
8473
assert(EQ.K(0).toString == "overridden", s"EQ.K(0).toString = ${EQ.K(0).toString}")
8574
assert(EQ.K(0).productPrefix == "noprefix", s"EQ.K(0).productPrefix = ${EQ.K(0).productPrefix}")
86-
assert(EQ.K(0).enumLabel == "K", s"EQ.K(0).enumLabel = ${EQ.K(0).enumLabel}")
8775
assert(Tag.IntTag.productPrefix == "", s"Tag.IntTag.productPrefix = ${Tag.IntTag.productPrefix}")
88-
assert(Tag.IntTag.enumLabel == "IntTag", s"Tag.IntTag.enumLabel = ${Tag.IntTag.enumLabel}")
8976
assert(
9077
assertion = Tag.IntTag.toString == s"${Tag.IntTag.getClass.getName}@${Integer.toHexString(123)}",
9178
message = s"Tag.IntTag.toString = ${Tag.IntTag.toString}"

tests/semanticdb/metac.expect

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ Schema => SemanticDB v4
641641
Uri => Enums.scala
642642
Text => empty
643643
Language => Scala
644-
Symbols => 183 entries
644+
Symbols => 181 entries
645645
Occurrences => 203 entries
646646

647647
Symbols:
@@ -710,7 +710,6 @@ _empty_/Enums.Maybe.Just#copy$default$1().[A] => typeparam A
710710
_empty_/Enums.Maybe.Just#copy(). => method copy
711711
_empty_/Enums.Maybe.Just#copy().(value) => param value
712712
_empty_/Enums.Maybe.Just#copy().[A] => typeparam A
713-
_empty_/Enums.Maybe.Just#enumLabel(). => method enumLabel
714713
_empty_/Enums.Maybe.Just#ordinal(). => method ordinal
715714
_empty_/Enums.Maybe.Just#value. => val method value
716715
_empty_/Enums.Maybe.Just. => final object Just
@@ -810,7 +809,6 @@ _empty_/Enums.`<:<`.Refl#[C] => typeparam C
810809
_empty_/Enums.`<:<`.Refl#`<init>`(). => primary ctor <init>
811810
_empty_/Enums.`<:<`.Refl#copy(). => method copy
812811
_empty_/Enums.`<:<`.Refl#copy().[C] => typeparam C
813-
_empty_/Enums.`<:<`.Refl#enumLabel(). => method enumLabel
814812
_empty_/Enums.`<:<`.Refl#ordinal(). => method ordinal
815813
_empty_/Enums.`<:<`.Refl. => final object Refl
816814
_empty_/Enums.`<:<`.Refl.apply(). => method apply

0 commit comments

Comments
 (0)