@@ -20,10 +20,18 @@ object DesugarEnums {
20
20
val Simple, Object, Class : Value = Value
21
21
}
22
22
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
+
23
31
/** Attachment containing the number of enum cases, the smallest kind that was seen so far,
24
32
* and a list of all the value cases with their ordinals.
25
33
*/
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 ()
27
35
28
36
/** Attachment signalling that when this definition is desugared, it should add any additional
29
37
* lookup methods for enums.
@@ -84,35 +92,55 @@ object DesugarEnums {
84
92
private def valuesDot (name : PreName )(implicit src : SourceFile ) =
85
93
Select (Ident (nme.DOLLAR_VALUES ), name.toTermName)
86
94
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 )
89
100
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 :
91
102
*
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 =
105
+ * scala.runtime.ScalaRuntime.wrapRefArray($values).map((x_0: E) => (x_0.enumLabel, x_0)).toMap
106
+ * def values = $values.clone
94
107
* def valueOf($name: String) =
95
- * try $values.fromName ($name) catch
108
+ * try $valuesReverse ($name) catch
96
109
* {
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)
99
112
* }
100
113
*/
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
102
116
val rawEnumClassRef = rawRef(enumClass.typeRef)
103
117
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
+
104
135
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_ ))
106
137
.withFlags(Synthetic )
107
- val privateValuesDef =
108
- ValDef (nme.DOLLAR_VALUES , TypeTree (), New (defn.EnumValuesClass .typeRef.ofRawEnum, ListOfNil ))
109
- .withFlags(Private | Synthetic )
110
138
111
139
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
+
114
142
val valuesOfBody = Try (
115
- expr = Apply (valuesDot( " fromName " ), Ident (nme.nameDollar) :: Nil ),
143
+ expr = Apply (Ident (nme. DOLLAR_VALUES_REVERSE ), Ident (nme.nameDollar) :: Nil ),
116
144
cases = CaseDef (
117
145
pat = Typed (Ident (nme.DEFAULT_EXCEPTION_NAME ), TypeTree (defn.NoSuchElementExceptionType )),
118
146
guard = EmptyTree ,
@@ -124,25 +152,32 @@ object DesugarEnums {
124
152
TypeTree (), valuesOfBody)
125
153
.withFlags(Synthetic )
126
154
127
- valuesDef ::
128
155
privateValuesDef ::
156
+ privateReverseValuesDef ::
157
+ valuesDef ::
129
158
valueOfDef :: Nil
130
159
}
131
160
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
146
181
147
182
/** A creation method for a value of enum type `E`, which is defined as follows:
148
183
*
@@ -167,7 +202,7 @@ object DesugarEnums {
167
202
parents = enumClassRef :: scalaRuntimeDot(tpnme.EnumValue ) :: Nil ,
168
203
derived = Nil ,
169
204
self = EmptyValDef ,
170
- body = fieldMethods ::: registerCall :: Nil
205
+ body = fieldMethods
171
206
).withAttachment(ExtendsSingletonMirror , ()))
172
207
DefDef (nme.DOLLAR_NEW , Nil ,
173
208
List (List (param(nme.ordinalDollar_, defn.IntType ), param(nme.nameDollar, defn.StringType ))),
@@ -279,25 +314,22 @@ object DesugarEnums {
279
314
* unless that scaffolding was already generated by a previous call to `nextEnumKind`.
280
315
*/
281
316
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
284
321
val cases = name match
285
322
case name : TermName => (ordinal, name) :: seenCases
286
323
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 )
297
329
}
298
330
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 )
301
333
302
334
private def isJavaEnum (using Context ): Boolean = ctx.owner.linkedClass.derivesFrom(defn.JavaEnumClass )
303
335
@@ -325,10 +357,10 @@ object DesugarEnums {
325
357
val enumLabelDef = enumLabelLit(name.toString)
326
358
val impl1 = cpy.Template (impl)(
327
359
parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue ),
328
- body = ordinalDef ::: enumLabelDef :: registerCall :: Nil
360
+ body = ordinalDef ::: enumLabelDef :: Nil
329
361
).withAttachment(ExtendsSingletonMirror , ())
330
362
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)
332
364
}
333
365
}
334
366
@@ -344,6 +376,6 @@ object DesugarEnums {
344
376
val (tag, scaffolding) = nextOrdinal(name, CaseKind .Simple , definesLookups)
345
377
val creator = Apply (Ident (nme.DOLLAR_NEW ), List (Literal (Constant (tag)), Literal (Constant (name.toString))))
346
378
val vdef = ValDef (name, enumClassRef, creator).withMods(mods.withAddedFlags(EnumValue , span))
347
- flatTree(scaffolding ::: vdef :: Nil ).withSpan(span)
379
+ flatTree(vdef :: scaffolding ).withSpan(span)
348
380
}
349
381
}
0 commit comments