Skip to content

Commit 22f8dd4

Browse files
committed
remove scala.runtime.EnumValues
1 parent 5dbd4ed commit 22f8dd4

File tree

9 files changed

+103
-84
lines changed

9 files changed

+103
-84
lines changed

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

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,18 @@ object DesugarEnums {
2020
val Simple, Object, Class: Value = Value
2121
}
2222

23+
final class EnumConstraints(minKind: CaseKind.Value, maxKind: CaseKind.Value, cases: List[(Int, TermName)]):
24+
require(minKind <= maxKind && !(cached && cachedValues.isEmpty))
25+
def requiresCreator = minKind == CaseKind.Simple
26+
def isEnumeration = maxKind < CaseKind.Class
27+
def cached = minKind < CaseKind.Class
28+
def cachedValues = cases
29+
end EnumConstraints
30+
2331
/** Attachment containing the number of enum cases, the smallest kind that was seen so far,
2432
* and a list of all the value cases with their ordinals.
2533
*/
26-
val EnumCaseCount: Property.Key[(Int, CaseKind.Value, List[(Int, TermName)])] = Property.Key()
34+
val EnumCaseCount: Property.Key[(Int, CaseKind.Value, CaseKind.Value, List[(Int, TermName)])] = Property.Key()
2735

2836
/** Attachment signalling that when this definition is desugared, it should add any additional
2937
* lookup methods for enums.
@@ -84,35 +92,55 @@ object DesugarEnums {
8492
private def valuesDot(name: PreName)(implicit src: SourceFile) =
8593
Select(Ident(nme.DOLLAR_VALUES), name.toTermName)
8694

87-
private def registerCall(using Context): Tree =
88-
Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil)
95+
private def ArrayLiteral(values: List[Tree], tpt: Tree)(using Context): Tree =
96+
val clazzOf = TypeApply(ref(defn.Predef_classOf.termRef), tpt :: Nil)
97+
val ctag = Apply(TypeApply(ref(defn.ClassTagModule_apply.termRef), tpt :: Nil), clazzOf :: Nil)
98+
val apply = Select(ref(defn.ArrayModule.termRef), nme.apply)
99+
Apply(Apply(TypeApply(apply, tpt :: Nil), values), ctag :: Nil)
89100

90-
/** The following lists of definitions for an enum type E:
101+
/** The following lists of definitions for an enum type E and known value cases e_0, ..., e_n:
91102
*
92-
* private val $values = new EnumValues[E]
93-
* def values = $values.values.toArray
103+
* private val $values = Array[E](e_0,...,e_n)(ClassTag[E](classOf[E]))
104+
* @annotation.threadUnsafe private lazy val $valuesReverse = Map[String, E] =
105+
* scala.runtime.ScalaRuntime.wrapRefArray($values).map((x_0: E) => (x_0.enumLabel, x_0)).toMap
106+
* def values = $values.clone
94107
* def valueOf($name: String) =
95-
* try $values.fromName($name) catch
108+
* try $valuesReverse($name) catch
96109
* {
97-
* case ex$:NoSuchElementException =>
98-
* throw new IllegalArgumentException("key not found: ".concat(name))
110+
* case ex$: NoSuchElementException =>
111+
* throw new IllegalArgumentException("enum case not found: " + $name)
99112
* }
100113
*/
101-
private def enumScaffolding(using Context): List[Tree] = {
114+
private def enumScaffolding(enumCases: List[(Int, TermName)])(using Context): List[Tree] = {
115+
import dotty.tools.dotc.transform.SymUtils.rawTypeRef
102116
val rawEnumClassRef = rawRef(enumClass.typeRef)
103117
extension (tpe: NamedType) def ofRawEnum = AppliedTypeTree(ref(tpe), rawEnumClassRef)
118+
119+
val privateValuesDef =
120+
ValDef(nme.DOLLAR_VALUES, TypeTree(),
121+
ArrayLiteral(enumCases.map((_, name) => Ident(name)), rawEnumClassRef))
122+
.withFlags(Private | Synthetic)
123+
124+
val privateReverseValuesDef =
125+
val wrapped = Apply(Select(ref(defn.ScalaRuntimeModule.termRef), nme.wrapRefArray), Ident(nme.DOLLAR_VALUES))
126+
val mapper =
127+
val paramName = nme.syntheticParamName(0)
128+
val paramDef = param(paramName, rawEnumClassRef)
129+
Function(paramDef :: Nil, Tuple(Select(Ident(paramName), nme.enumLabel) :: Ident(paramName) :: Nil))
130+
val mapBody = Select(Apply(Select(wrapped, nme.map), mapper), nme.toMap)
131+
val annot = New(ref(defn.ThreadUnsafeAnnot.typeRef), Nil).withSpan(ctx.tree.span)
132+
ValDef(nme.DOLLAR_VALUES_REVERSE, TypeTree(), mapBody)
133+
.withFlags(Private | Synthetic | Lazy).withAnnotations(annot :: Nil)
134+
104135
val valuesDef =
105-
DefDef(nme.values, Nil, Nil, defn.ArrayType.ofRawEnum, Select(valuesDot(nme.values), nme.toArray))
136+
DefDef(nme.values, Nil, Nil, defn.ArrayType.ofRawEnum, valuesDot(nme.clone_))
106137
.withFlags(Synthetic)
107-
val privateValuesDef =
108-
ValDef(nme.DOLLAR_VALUES, TypeTree(), New(defn.EnumValuesClass.typeRef.ofRawEnum, ListOfNil))
109-
.withFlags(Private | Synthetic)
110138

111139
val valuesOfExnMessage = Apply(
112-
Select(Literal(Constant("key not found: ")), "concat".toTermName),
113-
Ident(nme.nameDollar) :: Nil)
140+
Select(Literal(Constant("enum case not found: ")), nme.PLUS), Ident(nme.nameDollar))
141+
114142
val valuesOfBody = Try(
115-
expr = Apply(valuesDot("fromName"), Ident(nme.nameDollar) :: Nil),
143+
expr = Apply(Ident(nme.DOLLAR_VALUES_REVERSE), Ident(nme.nameDollar) :: Nil),
116144
cases = CaseDef(
117145
pat = Typed(Ident(nme.DEFAULT_EXCEPTION_NAME), TypeTree(defn.NoSuchElementExceptionType)),
118146
guard = EmptyTree,
@@ -124,25 +152,32 @@ object DesugarEnums {
124152
TypeTree(), valuesOfBody)
125153
.withFlags(Synthetic)
126154

127-
valuesDef ::
128155
privateValuesDef ::
156+
privateReverseValuesDef ::
157+
valuesDef ::
129158
valueOfDef :: Nil
130159
}
131160

132-
private def enumLookupMethods(cases: List[(Int, TermName)])(using Context): List[Tree] =
133-
if isJavaEnum || cases.isEmpty then Nil
134-
else
135-
val defaultCase =
136-
val ord = Ident(nme.ordinal)
137-
val err = Throw(New(TypeTree(defn.IndexOutOfBoundsException.typeRef), List(Select(ord, nme.toString_) :: Nil)))
138-
CaseDef(ord, EmptyTree, err)
139-
val valueCases = cases.map((i, name) =>
140-
CaseDef(Literal(Constant(i)), EmptyTree, Ident(name))
141-
) ::: defaultCase :: Nil
142-
val fromOrdinalDef = DefDef(nme.fromOrdinalDollar, Nil, List(param(nme.ordinalDollar_, defn.IntType) :: Nil),
143-
rawRef(enumClass.typeRef), Match(Ident(nme.ordinalDollar_), valueCases))
144-
.withFlags(Synthetic | Private)
145-
fromOrdinalDef :: Nil
161+
private def enumLookupMethods(constraints: EnumConstraints)(using Context): List[Tree] =
162+
def scaffolding: List[Tree] = if constraints.cached then enumScaffolding(constraints.cachedValues) else Nil
163+
def valueCtor: List[Tree] = if constraints.requiresCreator then enumValueCreator :: Nil else Nil
164+
def byOrdinal: List[Tree] =
165+
if isJavaEnum || !constraints.cached then Nil
166+
else
167+
val defaultCase =
168+
val ord = Ident(nme.ordinal)
169+
val err = Throw(New(TypeTree(defn.IndexOutOfBoundsException.typeRef), List(Select(ord, nme.toString_) :: Nil)))
170+
CaseDef(ord, EmptyTree, err)
171+
val valueCases = constraints.cachedValues.map((i, name) =>
172+
CaseDef(Literal(Constant(i)), EmptyTree, Ident(name))
173+
) ::: defaultCase :: Nil
174+
val fromOrdinalDef = DefDef(nme.fromOrdinalDollar, Nil, List(param(nme.ordinalDollar_, defn.IntType) :: Nil),
175+
rawRef(enumClass.typeRef), Match(Ident(nme.ordinalDollar_), valueCases))
176+
.withFlags(Synthetic | Private)
177+
fromOrdinalDef :: Nil
178+
179+
scaffolding ::: valueCtor ::: byOrdinal
180+
end enumLookupMethods
146181

147182
/** A creation method for a value of enum type `E`, which is defined as follows:
148183
*
@@ -167,7 +202,7 @@ object DesugarEnums {
167202
parents = enumClassRef :: scalaRuntimeDot(tpnme.EnumValue) :: Nil,
168203
derived = Nil,
169204
self = EmptyValDef,
170-
body = fieldMethods ::: registerCall :: Nil
205+
body = fieldMethods
171206
).withAttachment(ExtendsSingletonMirror, ()))
172207
DefDef(nme.DOLLAR_NEW, Nil,
173208
List(List(param(nme.ordinalDollar_, defn.IntType), param(nme.nameDollar, defn.StringType))),
@@ -279,25 +314,22 @@ object DesugarEnums {
279314
* unless that scaffolding was already generated by a previous call to `nextEnumKind`.
280315
*/
281316
def nextOrdinal(name: Name, kind: CaseKind.Value, definesLookups: Boolean)(using Context): (Int, List[Tree]) = {
282-
val (ordinal, seenKind, seenCases) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, CaseKind.Class, Nil))
283-
val minKind = if kind < seenKind then kind else seenKind
317+
val (ordinal, seenMinKind, seenMaxKind, seenCases) =
318+
ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, CaseKind.Class, CaseKind.Simple, Nil))
319+
val minKind = if kind < seenMinKind then kind else seenMinKind
320+
val maxKind = if kind > seenMaxKind then kind else seenMaxKind
284321
val cases = name match
285322
case name: TermName => (ordinal, name) :: seenCases
286323
case _ => seenCases
287-
ctx.tree.pushAttachment(EnumCaseCount, (ordinal + 1, minKind, cases))
288-
val scaffolding0 =
289-
if (kind >= seenKind) Nil
290-
else if (kind == CaseKind.Object) enumScaffolding
291-
else if (seenKind == CaseKind.Object) enumValueCreator :: Nil
292-
else enumScaffolding :+ enumValueCreator
293-
val scaffolding =
294-
if definesLookups then scaffolding0 ::: enumLookupMethods(cases.reverse)
295-
else scaffolding0
296-
(ordinal, scaffolding)
324+
if definesLookups then
325+
(ordinal, enumLookupMethods(EnumConstraints(minKind, maxKind, cases.reverse)))
326+
else
327+
ctx.tree.pushAttachment(EnumCaseCount, (ordinal + 1, minKind, maxKind, cases))
328+
(ordinal, Nil)
297329
}
298330

299-
def param(name: TermName, typ: Type)(using Context) =
300-
ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param)
331+
def param(name: TermName, typ: Type)(using Context): ValDef = param(name, TypeTree(typ))
332+
def param(name: TermName, tpt: Tree)(using Context): ValDef = ValDef(name, tpt, EmptyTree).withFlags(Param)
301333

302334
private def isJavaEnum(using Context): Boolean = ctx.owner.linkedClass.derivesFrom(defn.JavaEnumClass)
303335

@@ -325,10 +357,10 @@ object DesugarEnums {
325357
val enumLabelDef = enumLabelLit(name.toString)
326358
val impl1 = cpy.Template(impl)(
327359
parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue),
328-
body = ordinalDef ::: enumLabelDef :: registerCall :: Nil
360+
body = ordinalDef ::: enumLabelDef :: Nil
329361
).withAttachment(ExtendsSingletonMirror, ())
330362
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods.withAddedFlags(EnumValue, span))
331-
flatTree(scaffolding ::: vdef :: Nil).withSpan(span)
363+
flatTree(vdef :: scaffolding).withSpan(span)
332364
}
333365
}
334366

@@ -344,6 +376,6 @@ object DesugarEnums {
344376
val (tag, scaffolding) = nextOrdinal(name, CaseKind.Simple, definesLookups)
345377
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
346378
val vdef = ValDef(name, enumClassRef, creator).withMods(mods.withAddedFlags(EnumValue, span))
347-
flatTree(scaffolding ::: vdef :: Nil).withSpan(span)
379+
flatTree(vdef :: scaffolding).withSpan(span)
348380
}
349381
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ object StdNames {
123123
val DEFAULT_GETTER_INIT: N = "$lessinit$greater"
124124
val DO_WHILE_PREFIX: N = "doWhile$"
125125
val DOLLAR_VALUES: N = "$values"
126+
val DOLLAR_VALUES_REVERSE: N = "$valuesReverse"
126127
val DOLLAR_NEW: N = "$new"
127128
val EMPTY: N = ""
128129
val EMPTY_PACKAGE: N = "<empty>"
@@ -596,6 +597,7 @@ object StdNames {
596597
val toArray: N = "toArray"
597598
val toExpr: N = "toExpr"
598599
val toList: N = "toList"
600+
val toMap: N = "toMap"
599601
val toObjectArray : N = "toObjectArray"
600602
val toSeq: N = "toSeq"
601603
val toString_ : N = "toString"

docs/docs/reference/enums/desugarEnums.md

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,12 +121,10 @@ map into `case class`es or `val`s.
121121
```
122122
expands to a value definition in `E`'s companion object:
123123
```scala
124-
val C = new <parents> { <body>; def ordinal = n; $values.register(this) }
124+
val C = new <parents> { <body>; def ordinal = n }
125125
```
126126
where `n` is the ordinal number of the case in the companion object,
127-
starting from 0. The statement `$values.register(this)` registers the value
128-
as one of the `values` of the enumeration (see below). `$values` is a
129-
compiler-defined private value in the companion object. The anonymous class also
127+
starting from 0. The anonymous class also
130128
implements the abstract `Product` methods that it inherits from `Enum`.
131129

132130

@@ -162,8 +160,7 @@ An enum `E` (possibly generic) that defines one or more singleton cases
162160
will define the following additional synthetic members in its companion object (where `E'` denotes `E` with
163161
any type parameters replaced by wildcards):
164162

165-
- A method `valueOf(name: String): E'`. It returns the singleton case value whose
166-
`toString` representation is `name`.
163+
- A method `valueOf(name: String): E'`. It returns the singleton case value whose `enumLabel` is `name`.
167164
- A method `values` which returns an `Array[E']` of all singleton case
168165
values defined by `E`, in the order of their definitions.
169166

@@ -178,7 +175,6 @@ If `E` contains at least one simple case, its companion object will define in ad
178175
def enumLabel = $name
179176
override def productPrefix = enumLabel // if not overridden in `E`
180177
override def toString = enumLabel // if not overridden in `E`
181-
$values.register(this) // register enum value so that `valueOf` and `values` can return it.
182178
}
183179
```
184180

docs/docs/reference/enums/enums.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ val Venus: Planet =
136136
def enumLabel: String = "Venus"
137137
override def productPrefix: String = enumLabel
138138
override def toString: String = enumLabel
139-
// internal code to register value
140139
}
141140
```
142141

library/src-bootstrapped/scala/runtime/EnumValues.scala

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

tests/neg/enumsLabelDef.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
enum Labelled {
22

33
case A // error overriding method enumLabel in class Labelled of type => String;
4-
case B(arg: Int) // error overriding method enumLabel in class Labelled of type => String;
54

65
def enumLabel: String = "nolabel"
76
}
87

98
trait Mixin { def enumLabel: String = "mixin" }
109

1110
enum Mixed extends Mixin {
12-
case C // error overriding method enumLabel in trait Mixin of type => String;
11+
case B // error overriding method enumLabel in trait Mixin of type => String;
12+
}
13+
14+
enum MixedAlso {
15+
case C extends MixedAlso with Mixin // error overriding method enumLabel in trait Mixin of type => String;
1316
}
1417

1518
trait HasEnumLabel { def enumLabel: String }

tests/run/enum-java.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ MONDAY : 0
2626
TUESDAY : 1
2727
SATURDAY : 2
2828
By-name value: MONDAY
29-
Correctly failed to retrieve illegal name, message: key not found: stuff
29+
Correctly failed to retrieve illegal name, message: enum case not found: stuff
3030

3131
Collections Test
3232
Retrieving Monday: workday

tests/run/enums-java-compat.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ TUESDAY : 1
66
SATURDAY : 2
77
Stuff : 3
88
By-name value: MONDAY
9-
Correctly failed to retrieve illegal name, message: key not found: stuff
9+
Correctly failed to retrieve illegal name, message: enum case not found: stuff

tests/semanticdb/metac.expect

Lines changed: 9 additions & 1 deletion
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 => 191 entries
645645
Occurrences => 203 entries
646646

647647
Symbols:
@@ -654,6 +654,7 @@ _empty_/Enums.Coin. => final object Coin
654654
_empty_/Enums.Coin.$fromOrdinal(). => method $fromOrdinal
655655
_empty_/Enums.Coin.$fromOrdinal().(_$ordinal) => param _$ordinal
656656
_empty_/Enums.Coin.$values. => val method $values
657+
_empty_/Enums.Coin.$valuesReverse. => lazy val method $valuesReverse
657658
_empty_/Enums.Coin.Dime. => case val static enum method Dime
658659
_empty_/Enums.Coin.Dollar. => case val static enum method Dollar
659660
_empty_/Enums.Coin.Nickel. => case val static enum method Nickel
@@ -671,6 +672,7 @@ _empty_/Enums.Colour.$new(). => method $new
671672
_empty_/Enums.Colour.$new().($name) => param $name
672673
_empty_/Enums.Colour.$new().(_$ordinal) => param _$ordinal
673674
_empty_/Enums.Colour.$values. => val method $values
675+
_empty_/Enums.Colour.$valuesReverse. => lazy val method $valuesReverse
674676
_empty_/Enums.Colour.Blue. => case val static enum method Blue
675677
_empty_/Enums.Colour.Green. => case val static enum method Green
676678
_empty_/Enums.Colour.Red. => case val static enum method Red
@@ -686,6 +688,7 @@ _empty_/Enums.Directions.$new(). => method $new
686688
_empty_/Enums.Directions.$new().($name) => param $name
687689
_empty_/Enums.Directions.$new().(_$ordinal) => param _$ordinal
688690
_empty_/Enums.Directions.$values. => val method $values
691+
_empty_/Enums.Directions.$valuesReverse. => lazy val method $valuesReverse
689692
_empty_/Enums.Directions.East. => case val static enum method East
690693
_empty_/Enums.Directions.North. => case val static enum method North
691694
_empty_/Enums.Directions.South. => case val static enum method South
@@ -700,6 +703,7 @@ _empty_/Enums.Maybe. => final object Maybe
700703
_empty_/Enums.Maybe.$fromOrdinal(). => method $fromOrdinal
701704
_empty_/Enums.Maybe.$fromOrdinal().(_$ordinal) => param _$ordinal
702705
_empty_/Enums.Maybe.$values. => val method $values
706+
_empty_/Enums.Maybe.$valuesReverse. => lazy val method $valuesReverse
703707
_empty_/Enums.Maybe.Just# => final case enum class Just
704708
_empty_/Enums.Maybe.Just#[A] => typeparam A
705709
_empty_/Enums.Maybe.Just#_1(). => method _1
@@ -737,6 +741,7 @@ _empty_/Enums.Planet#surfaceWeight(). => method surfaceWeight
737741
_empty_/Enums.Planet#surfaceWeight().(otherMass) => param otherMass
738742
_empty_/Enums.Planet. => final object Planet
739743
_empty_/Enums.Planet.$values. => val method $values
744+
_empty_/Enums.Planet.$valuesReverse. => lazy val method $valuesReverse
740745
_empty_/Enums.Planet.Earth. => case val static enum method Earth
741746
_empty_/Enums.Planet.Jupiter. => case val static enum method Jupiter
742747
_empty_/Enums.Planet.Mars. => case val static enum method Mars
@@ -757,6 +762,7 @@ _empty_/Enums.Suits.$new(). => method $new
757762
_empty_/Enums.Suits.$new().($name) => param $name
758763
_empty_/Enums.Suits.$new().(_$ordinal) => param _$ordinal
759764
_empty_/Enums.Suits.$values. => val method $values
765+
_empty_/Enums.Suits.$valuesReverse. => lazy val method $valuesReverse
760766
_empty_/Enums.Suits.Clubs. => case val static enum method Clubs
761767
_empty_/Enums.Suits.Diamonds. => case val static enum method Diamonds
762768
_empty_/Enums.Suits.Hearts. => case val static enum method Hearts
@@ -776,6 +782,7 @@ _empty_/Enums.Tag. => final object Tag
776782
_empty_/Enums.Tag.$fromOrdinal(). => method $fromOrdinal
777783
_empty_/Enums.Tag.$fromOrdinal().(_$ordinal) => param _$ordinal
778784
_empty_/Enums.Tag.$values. => val method $values
785+
_empty_/Enums.Tag.$valuesReverse. => lazy val method $valuesReverse
779786
_empty_/Enums.Tag.BooleanTag. => case val static enum method BooleanTag
780787
_empty_/Enums.Tag.IntTag. => case val static enum method IntTag
781788
_empty_/Enums.Tag.valueOf(). => method valueOf
@@ -790,6 +797,7 @@ _empty_/Enums.WeekDays.$new(). => method $new
790797
_empty_/Enums.WeekDays.$new().($name) => param $name
791798
_empty_/Enums.WeekDays.$new().(_$ordinal) => param _$ordinal
792799
_empty_/Enums.WeekDays.$values. => val method $values
800+
_empty_/Enums.WeekDays.$valuesReverse. => lazy val method $valuesReverse
793801
_empty_/Enums.WeekDays.Friday. => case val static enum method Friday
794802
_empty_/Enums.WeekDays.Monday. => case val static enum method Monday
795803
_empty_/Enums.WeekDays.Saturday. => case val static enum method Saturday

0 commit comments

Comments
 (0)