diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f22b52754299..31080fa50132 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -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 { diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 67ab44c639e9..997caeec61a8 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -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, @@ -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) @@ -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) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index dc0bc7938255..9b0468437465 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -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" diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 982ba4c00b22..4c4e15bfdeb0 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -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)) @@ -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 { @@ -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) diff --git a/docs/docs/reference/enums/desugarEnums.md b/docs/docs/reference/enums/desugarEnums.md index b7d29dd3c2b8..204dd9ad57c1 100644 --- a/docs/docs/reference/enums/desugarEnums.md +++ b/docs/docs/reference/enums/desugarEnums.md @@ -34,12 +34,12 @@ map into `case class`es or `val`s. ```scala enum E ... { } ``` - 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 `` of all cases so that they can be used without prefix in the class. ```scala - sealed abstract class E ... extends with scala.Enum { + sealed abstract class E ... extends with scala.reflect.Enum { import E.{ } } @@ -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. @@ -177,9 +177,8 @@ 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` } ``` @@ -187,7 +186,7 @@ The anonymous class also implements the abstract `Product` methods that it inher 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 diff --git a/docs/docs/reference/enums/enums.md b/docs/docs/reference/enums/enums.md index 0553ac6d9e31..d5134b3c12cc 100644 --- a/docs/docs/reference/enums/enums.md +++ b/docs/docs/reference/enums/enums.md @@ -110,7 +110,7 @@ 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 @@ -118,9 +118,6 @@ 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 } @@ -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" } ``` diff --git a/library/src-bootstrapped/scala/reflect/Enum.scala b/library/src-bootstrapped/scala/reflect/Enum.scala index 8661e419f1ee..7296ca8becd2 100644 --- a/library/src-bootstrapped/scala/reflect/Enum.scala +++ b/library/src-bootstrapped/scala/reflect/Enum.scala @@ -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 diff --git a/tests/neg/enumsLabel-overrides.scala b/tests/neg/enumsLabel-overrides.scala deleted file mode 100644 index 4b7036b4785f..000000000000 --- a/tests/neg/enumsLabel-overrides.scala +++ /dev/null @@ -1,15 +0,0 @@ -trait Mixin { def enumLabel: String = "mixin" } - -enum Mixed extends Mixin { - case B // error: overriding method enumLabel in trait Mixin of type => String; -} - -enum MixedAlso { - case C extends MixedAlso with Mixin // error: overriding method enumLabel in trait Mixin of type => String; -} - -trait HasEnumLabel { def enumLabel: String } - -enum MyEnum extends HasEnumLabel { - case D // ok -} diff --git a/tests/neg/enumsLabel-singleimpl.scala b/tests/neg/enumsLabel-singleimpl.scala index 225519d5dcd9..8f72a6cc71f2 100644 --- a/tests/neg/enumsLabel-singleimpl.scala +++ b/tests/neg/enumsLabel-singleimpl.scala @@ -1,11 +1,3 @@ -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 @@ -13,3 +5,9 @@ enum Ordinalled { 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 +} diff --git a/tests/pos/enum-List-control.scala b/tests/pos/enum-List-control.scala index 99fce35c032c..dbe4b2a48f42 100644 --- a/tests/pos/enum-List-control.scala +++ b/tests/pos/enum-List-control.scala @@ -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 @@ -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() diff --git a/tests/run/enum-custom-toString.scala b/tests/run/enum-custom-toString.scala index 4c2007fbfe1c..54605d388524 100644 --- a/tests/run/enum-custom-toString.scala +++ b/tests/run/enum-custom-toString.scala @@ -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}" diff --git a/tests/semanticdb/metac.expect b/tests/semanticdb/metac.expect index 0cc17335b10a..0cdc0db3dce7 100644 --- a/tests/semanticdb/metac.expect +++ b/tests/semanticdb/metac.expect @@ -641,7 +641,7 @@ Schema => SemanticDB v4 Uri => Enums.scala Text => empty Language => Scala -Symbols => 183 entries +Symbols => 181 entries Occurrences => 203 entries Symbols: @@ -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 @@ -810,7 +809,6 @@ _empty_/Enums.`<:<`.Refl#[C] => typeparam C _empty_/Enums.`<:<`.Refl#``(). => primary ctor _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