Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop enumLabel method from enums #9989

Merged
merged 3 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ object desugar {
def enumCaseMeths =
if isEnumCase then
val (ordinal, scaffolding) = nextOrdinal(className, CaseKind.Class, definesEnumLookupMethods(cdef))
(ordinalMethLit(ordinal) :: enumLabelLit(className.toString) :: Nil, scaffolding)
(ordinalMethLit(ordinal) :: Nil, scaffolding)
else (Nil, Nil)
def copyMeths = {
val hasRepeatedParam = constrVparamss.nestedExists {
Expand Down
23 changes: 3 additions & 20 deletions compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
Original file line number Diff line number Diff line change
Expand Up @@ -175,20 +175,11 @@ object DesugarEnums {
/** A creation method for a value of enum type `E`, which is defined as follows:
*
* private def $new(_$ordinal: Int, $name: String) = new E with scala.runtime.EnumValue {
* def ordinal = _$ordinal // if `E` does not derive from `java.lang.Enum`
* def enumLabel = $name // if `E` does not derive from `java.lang.Enum`
* def enumLabel = this.name // if `E` derives from `java.lang.Enum`
* def ordinal = _$ordinal // if `E` does not derive from `java.lang.Enum`
* }
*/
private def enumValueCreator(using Context) = {
val fieldMethods =
if isJavaEnum then
val enumLabelDef = enumLabelMeth(Select(This(Ident(tpnme.EMPTY)), nme.name))
enumLabelDef :: Nil
else
val ordinalDef = ordinalMeth(Ident(nme.ordinalDollar_))
val enumLabelDef = enumLabelMeth(Ident(nme.nameDollar))
ordinalDef :: enumLabelDef :: Nil
val fieldMethods = if isJavaEnum then Nil else ordinalMeth(Ident(nme.ordinalDollar_)) :: Nil
val creator = New(Template(
constr = emptyConstructor,
parents = enumClassRef :: scalaRuntimeDot(tpnme.EnumValue) :: Nil,
Expand Down Expand Up @@ -284,15 +275,9 @@ object DesugarEnums {
def ordinalMeth(body: Tree)(using Context): DefDef =
DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withAddedFlags(Synthetic)

def enumLabelMeth(body: Tree)(using Context): DefDef =
DefDef(nme.enumLabel, Nil, Nil, TypeTree(defn.StringType), body).withAddedFlags(Synthetic)

def ordinalMethLit(ord: Int)(using Context): DefDef =
ordinalMeth(Literal(Constant(ord)))

def enumLabelLit(name: String)(using Context): DefDef =
enumLabelMeth(Literal(Constant(name)))

/** Expand a module definition representing a parameterless enum case */
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, definesLookups: Boolean, span: Span)(using Context): Tree = {
assert(impl.body.isEmpty)
Expand All @@ -301,11 +286,9 @@ object DesugarEnums {
expandSimpleEnumCase(name, mods, definesLookups, span)
else {
val (tag, scaffolding) = nextOrdinal(name, CaseKind.Object, definesLookups)
val ordinalDef = if isJavaEnum then Nil else ordinalMethLit(tag) :: Nil
val enumLabelDef = enumLabelLit(name.toString)
val impl1 = cpy.Template(impl)(
parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue),
body = ordinalDef ::: enumLabelDef :: Nil
body = if isJavaEnum then Nil else ordinalMethLit(tag) :: Nil
).withAttachment(ExtendsSingletonMirror, ())
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods.withAddedFlags(EnumValue, span))
flatTree(vdef :: scaffolding).withSpan(span)
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,6 @@ object StdNames {
val emptyValDef: N = "emptyValDef"
val end: N = "end"
val ensureAccessible : N = "ensureAccessible"
val enumLabel: N = "enumLabel"
val eq: N = "eq"
val eqInstance: N = "eqInstance"
val equalsNumChar : N = "equalsNumChar"
Expand Down
21 changes: 16 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,9 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased`
else clazz.caseAccessors
val isEnumValue = clazz.isAnonymousClass && clazz.classParents.head.classSymbol.is(Enum)
val isNonJavaEnumValue = isEnumValue && !clazz.derivesFrom(defn.JavaEnumClass)
val isSimpleEnumValue = isEnumValue && !clazz.owner.isAllOf(EnumCase)
val isJavaEnumValue = isEnumValue && clazz.derivesFrom(defn.JavaEnumClass)
val isNonJavaEnumValue = isEnumValue && !isJavaEnumValue

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

def callEnumLabel: Tree =
Select(This(clazz), nme.enumLabel).ensureApplied
def nameRef: Tree =
if isJavaEnumValue then
Select(This(clazz), nme.name).ensureApplied
else
identifierRef

def identifierRef: Tree =
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
ref(clazz.owner.paramSymss.head.find(_.name == nme.nameDollar).get)
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
Literal(Constant(clazz.owner.name.toString))

def toStringBody(vrefss: List[List[Tree]]): Tree =
if (clazz.is(ModuleClass)) ownName
else if (isNonJavaEnumValue) callEnumLabel
else if (isNonJavaEnumValue) identifierRef
else forwardToRuntime(vrefss.head)

def syntheticRHS(vrefss: List[List[Tree]])(using Context): Tree = synthetic.name match {
Expand All @@ -137,7 +148,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
case nme.equals_ => equalsBody(vrefss.head.head)
case nme.canEqual_ => canEqualBody(vrefss.head.head)
case nme.productArity => Literal(Constant(accessors.length))
case nme.productPrefix if isEnumValue => callEnumLabel
case nme.productPrefix if isEnumValue => nameRef
case nme.productPrefix => ownName
case nme.productElement => productElementBody(accessors.length, vrefss.head.head)
case nme.productElementName => productElementNameBody(accessors.length, vrefss.head.head)
Expand Down
13 changes: 6 additions & 7 deletions docs/docs/reference/enums/desugarEnums.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@ map into `case class`es or `val`s.
```scala
enum E ... { <defs> <cases> }
```
expands to a `sealed abstract` class that extends the `scala.Enum` trait and
expands to a `sealed abstract` class that extends the `scala.reflect.Enum` trait and
an associated companion object that contains the defined cases, expanded according
to rules (2 - 8). The enum class starts with a compiler-generated import that imports
the names `<caseIds>` of all cases so that they can be used without prefix in the class.
```scala
sealed abstract class E ... extends <parents> with scala.Enum {
sealed abstract class E ... extends <parents> with scala.reflect.Enum {
import E.{ <caseIds> }
<defs>
}
Expand Down Expand Up @@ -165,7 +165,7 @@ An enum `E` (possibly generic) that defines one or more singleton cases
will define the following additional synthetic members in its companion object (where `E'` denotes `E` with
any type parameters replaced by wildcards):

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

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

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

### Scopes for Enum Cases

Expand Down
10 changes: 3 additions & 7 deletions docs/docs/reference/enums/enums.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,14 @@ For a more in-depth example of using Scala 3 enums from Java, see [this test](ht
### Implementation

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

```scala
package scala.reflect

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

/** A string uniquely identifying a case of an enum */
def enumLabel: String

/** A number uniquely identifying a case of an enum */
def ordinal: Int
}
Expand All @@ -133,9 +130,8 @@ For instance, the `Venus` value above would be defined like this:
val Venus: Planet =
new Planet(4.869E24, 6051800.0) {
def ordinal: Int = 1
def enumLabel: String = "Venus"
override def productPrefix: String = enumLabel
override def toString: String = enumLabel
override def productPrefix: String = "Venus"
override def toString: String = "Venus"
}
```

Expand Down
3 changes: 0 additions & 3 deletions library/src-bootstrapped/scala/reflect/Enum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,5 @@ package scala.reflect
/** A base trait of all Scala enum definitions */
super trait Enum extends Any, Product, Serializable:

/** A string uniquely identifying a case of an enum */
def enumLabel: String

/** A number uniquely identifying a case of an enum */
def ordinal: Int
15 changes: 0 additions & 15 deletions tests/neg/enumsLabel-overrides.scala

This file was deleted.

14 changes: 6 additions & 8 deletions tests/neg/enumsLabel-singleimpl.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
enum Labelled {

case A // error: method enumLabel of type => String needs `override` modifier

def enumLabel: String = "nolabel"

}

enum Ordinalled {

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

def ordinal: Int = -1

}

trait HasOrdinal { def ordinal: Int = 23 }

enum MyEnum extends HasOrdinal {
case Foo // error: method ordinal of type => Int needs `override` modifier
}
2 changes: 0 additions & 2 deletions tests/pos/enum-List-control.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ abstract sealed class List[T] extends reflect.Enum
object List {
final class Cons[T](x: T, xs: List[T]) extends List[T] {
def ordinal = 0
def enumLabel = "Cons"
def canEqual(that: Any): Boolean = that.isInstanceOf[Cons[_]]
def productArity: Int = 2
def productElement(n: Int): Any = n match
Expand All @@ -14,7 +13,6 @@ object List {
}
final class Nil[T]() extends List[T], runtime.EnumValue {
def ordinal = 1
def enumLabel = "Nil"
}
object Nil {
def apply[T](): List[T] = new Nil()
Expand Down
13 changes: 0 additions & 13 deletions tests/run/enum-custom-toString.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,52 +40,39 @@ abstract class Tag[T] extends Enum
object Tag:
private final class IntTagImpl extends Tag[Int] with runtime.EnumValue:
def ordinal = 0
def enumLabel = "IntTag"
override def hashCode = 123
final val IntTag: Tag[Int] = IntTagImpl()

@main def Test =
assert(ES.A.toString == "overridden", s"ES.A.toString = ${ES.A.toString}")
assert(ES.A.productPrefix == "A", s"ES.A.productPrefix = ${ES.A.productPrefix}")
assert(ES.A.enumLabel == "A", s"ES.A.enumLabel = ${ES.A.enumLabel}")
assert(ES.valueOf("A") == ES.A, s"ES.valueOf(A) = ${ES.valueOf("A")}")
assert(EJ.B.toString == "overridden", s"EJ.B.toString = ${EJ.B.toString}")
assert(EJ.B.productPrefix == "B", s"EJ.B.productPrefix = ${EJ.B.productPrefix}")
assert(EJ.B.enumLabel == "B", s"EJ.B.enumLabel = ${EJ.B.enumLabel}")
assert(EJ.valueOf("B") == EJ.B, s"EJ.valueOf(B) = ${EJ.valueOf("B")}")
assert(EM.C.toString == "overridden", s"EM.C.toString = ${EM.C.toString}")
assert(EM.C.productPrefix == "noprefix", s"EM.C.productPrefix = ${EM.C.productPrefix}")
assert(EM.C.enumLabel == "C", s"EM.C.enumLabel = ${EM.C.enumLabel}")
assert(EM.valueOf("C") == EM.C, s"EM.valueOf(C) = ${EM.valueOf("C")}")
assert(ET.D.toString == "overridden", s"ET.D.toString = ${ET.D.toString}")
assert(ET.D.productPrefix == "D", s"ET.D.productPrefix = ${ET.D.productPrefix}")
assert(ET.D.enumLabel == "D", s"ET.D.enumLabel = ${ET.D.enumLabel}")
assert(EZ.E(0).toString == "overridden", s"EZ.E(0).toString = ${EZ.E(0).toString}")
assert(EZ.E(0).productPrefix == "E", s"EZ.E(0).productPrefix = ${EZ.E(0).productPrefix}")
assert(EZ.E(0).enumLabel == "E", s"EZ.E(0).enumLabel = ${EZ.E(0).enumLabel}")
assert(EC.F.toString == "F", s"EC.F.toString = ${EC.F.toString}")
assert(EC.F.productPrefix == "F", s"EC.F.productPrefix = ${EC.F.productPrefix}")
assert(EC.F.enumLabel == "F", s"EC.F.enumLabel = ${EC.F.enumLabel}")
assert(EC.valueOf("F") == EC.F, s"EC.valueOf(F) = ${EC.valueOf("F")}")
assert(EC.G(0).toString == "G(0)", s"EC.G(0).toString = ${EC.G(0).toString}")
assert(EC.G(0).productPrefix == "G", s"EC.G(0).productPrefix = ${EC.G(0).productPrefix}")
assert(EC.G(0).enumLabel == "G", s"EC.G(0).enumLabel = ${EC.G(0).enumLabel}")
assert(EO.H.toString == "overridden", s"EO.H.toString = ${EO.H.toString}")
assert(EO.H.productPrefix == "noprefix", s"EO.H.productPrefix = ${EO.H.productPrefix}")
assert(EO.H.enumLabel == "H", s"EO.H.enumLabel = ${EO.H.enumLabel}")
assert(EO.valueOf("H") == EO.H, s"EO.valueOf(H) = ${EO.valueOf("H")}")
assert(EO.I(0).toString == "overridden", s"EO.I(0).toString = ${EO.I(0).toString}")
assert(EO.I(0).productPrefix == "noprefix", s"EO.I(0).productPrefix = ${EO.I(0).productPrefix}")
assert(EO.I(0).enumLabel == "I", s"EO.I(0).enumLabel = ${EO.I(0).enumLabel}")
assert(EQ.J.toString == "overridden", s"EQ.J.toString = ${EQ.J.toString}")
assert(EQ.J.productPrefix == "noprefix", s"EQ.J.productPrefix = ${EQ.J.productPrefix}")
assert(EQ.J.enumLabel == "J", s"EQ.J.enumLabel = ${EQ.J.enumLabel}")
assert(EQ.valueOf("J") == EQ.J, s"EQ.valueOf(J) = ${EQ.valueOf("J")}")
assert(EQ.K(0).toString == "overridden", s"EQ.K(0).toString = ${EQ.K(0).toString}")
assert(EQ.K(0).productPrefix == "noprefix", s"EQ.K(0).productPrefix = ${EQ.K(0).productPrefix}")
assert(EQ.K(0).enumLabel == "K", s"EQ.K(0).enumLabel = ${EQ.K(0).enumLabel}")
assert(Tag.IntTag.productPrefix == "", s"Tag.IntTag.productPrefix = ${Tag.IntTag.productPrefix}")
assert(Tag.IntTag.enumLabel == "IntTag", s"Tag.IntTag.enumLabel = ${Tag.IntTag.enumLabel}")
assert(
assertion = Tag.IntTag.toString == s"${Tag.IntTag.getClass.getName}@${Integer.toHexString(123)}",
message = s"Tag.IntTag.toString = ${Tag.IntTag.toString}"
Expand Down
4 changes: 1 addition & 3 deletions tests/semanticdb/metac.expect
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ Schema => SemanticDB v4
Uri => Enums.scala
Text => empty
Language => Scala
Symbols => 183 entries
Symbols => 181 entries
Occurrences => 203 entries

Symbols:
Expand Down Expand Up @@ -710,7 +710,6 @@ _empty_/Enums.Maybe.Just#copy$default$1().[A] => typeparam A
_empty_/Enums.Maybe.Just#copy(). => method copy
_empty_/Enums.Maybe.Just#copy().(value) => param value
_empty_/Enums.Maybe.Just#copy().[A] => typeparam A
_empty_/Enums.Maybe.Just#enumLabel(). => method enumLabel
_empty_/Enums.Maybe.Just#ordinal(). => method ordinal
_empty_/Enums.Maybe.Just#value. => val method value
_empty_/Enums.Maybe.Just. => final object Just
Expand Down Expand Up @@ -810,7 +809,6 @@ _empty_/Enums.`<:<`.Refl#[C] => typeparam C
_empty_/Enums.`<:<`.Refl#`<init>`(). => primary ctor <init>
_empty_/Enums.`<:<`.Refl#copy(). => method copy
_empty_/Enums.`<:<`.Refl#copy().[C] => typeparam C
_empty_/Enums.`<:<`.Refl#enumLabel(). => method enumLabel
_empty_/Enums.`<:<`.Refl#ordinal(). => method ordinal
_empty_/Enums.`<:<`.Refl. => final object Refl
_empty_/Enums.`<:<`.Refl.apply(). => method apply
Expand Down