@@ -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 =
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}
0 commit comments