@@ -10,26 +10,26 @@ import collection.mutable.ListBuffer
10
10
import util .Property
11
11
import typer .ErrorReporting ._
12
12
13
+ /** Helper methods to desugar enums */
13
14
object DesugarEnums {
14
15
import untpd ._
15
16
import desugar .DerivedFromParamTree
16
17
17
- /** Attachment containing: The number of enum cases seen so far, and whether a
18
- * simple enum case was already seen.
19
- */
20
- val EnumCaseCount = new Property .Key [(Int , Boolean )]
18
+ @ sharable object CaseKind extends Enumeration {
19
+ val Simple, Object, Class = Value
20
+ }
21
21
22
- def enumClass (implicit ctx : Context ) = ctx.owner.linkedClass
22
+ /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */
23
+ val EnumCaseCount = new Property .Key [(Int , CaseKind .Value )]
23
24
24
- def nextEnumTag (isSimpleCase : Boolean )(implicit ctx : Context ): (Int , Boolean ) = {
25
- val (count, simpleSeen) = ctx.tree.removeAttachment(EnumCaseCount ).getOrElse((0 , false ))
26
- ctx.tree.pushAttachment(EnumCaseCount , (count + 1 , simpleSeen | isSimpleCase))
27
- (count, simpleSeen)
28
- }
25
+ /** the enumeration class that is a companion of the current object */
26
+ def enumClass (implicit ctx : Context ) = ctx.owner.linkedClass
29
27
28
+ /** Is this an enum case that's situated in a companion object of an enum class? */
30
29
def isLegalEnumCase (tree : MemberDef )(implicit ctx : Context ): Boolean =
31
30
tree.mods.hasMod[Mod .EnumCase ] && enumCaseIsLegal(tree)
32
31
32
+ /** Is enum case `tree` situated in a companion object of an enum class? */
33
33
def enumCaseIsLegal (tree : Tree )(implicit ctx : Context ): Boolean = (
34
34
ctx.owner.is(ModuleClass ) && enumClass.derivesFrom(defn.EnumClass )
35
35
|| { ctx.error(em " case not allowed here, since owner ${ctx.owner} is not an `enum' object " , tree.pos)
@@ -81,59 +81,89 @@ object DesugarEnums {
81
81
TypeTree (enumClass.typeRef.appliedTo(targs)).withPos(pos)
82
82
}
83
83
84
- def enumTagMeth (implicit ctx : Context ) =
85
- DefDef (nme.enumTag, Nil , Nil , TypeTree (),
86
- Literal (Constant (nextEnumTag(isSimpleCase = false )._1)))
87
-
84
+ /** A type tree referring to `enumClass` */
88
85
def enumClassRef (implicit ctx : Context ) = TypeTree (enumClass.typeRef)
89
86
87
+ /** Add implied flags to an enum class or an enum case */
90
88
def addEnumFlags (cdef : TypeDef )(implicit ctx : Context ) =
91
89
if (cdef.mods.hasMod[Mod .Enum ]) cdef.withFlags(cdef.mods.flags | Abstract | Sealed )
92
90
else if (isLegalEnumCase(cdef)) cdef.withFlags(cdef.mods.flags | Final )
93
91
else cdef
94
92
93
+ private def valuesDot (name : String ) = Select (Ident (nme.DOLLAR_VALUES ), name.toTermName)
94
+ private def registerCall = Apply (valuesDot(" register" ), This (EmptyTypeIdent ) :: Nil )
95
+
95
96
/** The following lists of definitions for an enum type E:
96
97
*
97
98
* private val $values = new EnumValues[E]
98
- * def valueOf = $values.fromInt
99
- * def withName = $values.fromName
100
- * def values = $values.values
101
- *
99
+ * def enumValue = $values.fromInt
100
+ * def enumValueNamed = $values.fromName
101
+ * def enumValues = $values.values
102
+ */
103
+ private def enumScaffolding (implicit ctx : Context ): List [Tree ] = {
104
+ val enumType = enumClass.typeRef.appliedTo(enumClass.typeParams.map(_ => TypeBounds .empty))
105
+ def enumDefDef (name : String , select : String ) =
106
+ DefDef (name.toTermName, Nil , Nil , TypeTree (), valuesDot(select))
107
+ val privateValuesDef =
108
+ ValDef (nme.DOLLAR_VALUES , TypeTree (),
109
+ New (TypeTree (defn.EnumValuesType .appliedTo(enumType :: Nil )), ListOfNil ))
110
+ .withFlags(Private )
111
+ val valueOfDef = enumDefDef(" enumValue" , " fromInt" )
112
+ val withNameDef = enumDefDef(" enumValueNamed" , " fromName" )
113
+ val valuesDef = enumDefDef(" enumValues" , " values" )
114
+ List (privateValuesDef, valueOfDef, withNameDef, valuesDef)
115
+ }
116
+
117
+ /** A creation method for a value of enum type `E`, which is defined as follows:
118
+ *
102
119
* private def $new(tag: Int, name: String) = new E {
103
120
* def enumTag = tag
104
121
* override def toString = name
105
122
* $values.register(this)
106
123
* }
107
124
*/
108
- private def enumScaffolding (implicit ctx : Context ): List [Tree ] = {
109
- def valuesDot (name : String ) = Select (Ident (nme.DOLLAR_VALUES ), name.toTermName)
110
- def enumDefDef (name : String , select : String ) =
111
- DefDef (name.toTermName, Nil , Nil , TypeTree (), valuesDot(select))
125
+ private def enumValueCreator (implicit ctx : Context ) = {
112
126
def param (name : TermName , typ : Type ) =
113
127
ValDef (name, TypeTree (typ), EmptyTree ).withFlags(Param )
114
- val privateValuesDef =
115
- ValDef (nme.DOLLAR_VALUES , TypeTree (),
116
- New (TypeTree (defn.EnumValuesType .appliedTo(enumClass.typeRef :: Nil )), ListOfNil ))
117
- .withFlags(Private )
118
- val valueOfDef = enumDefDef(" valueOf" , " fromInt" )
119
- val withNameDef = enumDefDef(" withName" , " fromName" )
120
- val valuesDef = enumDefDef(" values" , " values" )
121
128
val enumTagDef =
122
129
DefDef (nme.enumTag, Nil , Nil , TypeTree (), Ident (nme.tag))
123
130
val toStringDef =
124
131
DefDef (nme.toString_, Nil , Nil , TypeTree (), Ident (nme.name))
125
132
.withFlags(Override )
126
- val registerStat =
127
- Apply (valuesDot(" register" ), This (EmptyTypeIdent ) :: Nil )
128
133
def creator = New (Template (emptyConstructor, enumClassRef :: Nil , EmptyValDef ,
129
- List (enumTagDef, toStringDef, registerStat)))
130
- val newDef =
131
- DefDef (nme.DOLLAR_NEW , Nil ,
132
- List (List (param(nme.tag, defn.IntType ), param(nme.name, defn.StringType ))),
133
- TypeTree (), creator)
134
- List (privateValuesDef, valueOfDef, withNameDef, valuesDef, newDef)
134
+ List (enumTagDef, toStringDef, registerCall)))
135
+ DefDef (nme.DOLLAR_NEW , Nil ,
136
+ List (List (param(nme.tag, defn.IntType ), param(nme.name, defn.StringType ))),
137
+ TypeTree (), creator)
138
+ }
139
+
140
+ /** A pair consisting of
141
+ * - the next enum tag
142
+ * - scaffolding containing the necessary definitions for singleton enum cases
143
+ * unless that scaffolding was already generated by a previous call to `nextEnumKind`.
144
+ */
145
+ def nextEnumTag (kind : CaseKind .Value )(implicit ctx : Context ): (Int , List [Tree ]) = {
146
+ val (count, seenKind) = ctx.tree.removeAttachment(EnumCaseCount ).getOrElse((0 , CaseKind .Class ))
147
+ val minKind = if (kind < seenKind) kind else seenKind
148
+ ctx.tree.pushAttachment(EnumCaseCount , (count + 1 , minKind))
149
+ val scaffolding =
150
+ if (kind >= seenKind) Nil
151
+ else if (kind == CaseKind .Object ) enumScaffolding
152
+ else if (seenKind == CaseKind .Object ) enumValueCreator :: Nil
153
+ else enumScaffolding :+ enumValueCreator
154
+ (count, scaffolding)
155
+ }
156
+
157
+ /** A pair consisting of
158
+ * - a method returning the next enum tag
159
+ * - scaffolding as defined in `nextEnumTag`
160
+ */
161
+ def enumTagMeth (kind : CaseKind .Value )(implicit ctx : Context ): (DefDef , List [Tree ]) = {
162
+ val (tag, scaffolding) = nextEnumTag(kind)
163
+ (DefDef (nme.enumTag, Nil , Nil , TypeTree (), Literal (Constant (tag))), scaffolding)
135
164
}
136
165
166
+ /** Expand a module definition representing a parameterless enum case */
137
167
def expandEnumModule (name : TermName , impl : Template , mods : Modifiers , pos : Position )(implicit ctx : Context ): Tree =
138
168
if (impl.parents.isEmpty)
139
169
if (impl.body.isEmpty)
@@ -146,22 +176,23 @@ object DesugarEnums {
146
176
def toStringMeth =
147
177
DefDef (nme.toString_, Nil , Nil , TypeTree (defn.StringType ), Literal (Constant (name.toString)))
148
178
.withFlags(Override )
149
- val impl1 = cpy.Template (impl)(body =
150
- impl.body ++ List (enumTagMeth, toStringMeth))
151
- ValDef (name, TypeTree (), New (impl1)).withMods(mods | Final ).withPos(pos)
179
+ val (tagMeth, scaffolding) = enumTagMeth(CaseKind .Object )
180
+ val impl1 = cpy.Template (impl)(body = impl.body ++ List (tagMeth, toStringMeth, registerCall))
181
+ val vdef = ValDef (name, TypeTree (), New (impl1)).withMods(mods | Final ).withPos(pos)
182
+ flatTree(scaffolding ::: vdef :: Nil ).withPos(pos.startPos)
152
183
}
153
184
185
+ /** Expand a simple enum case */
154
186
def expandSimpleEnumCase (name : TermName , mods : Modifiers , pos : Position )(implicit ctx : Context ): Tree =
155
- if (reconstitutedEnumTypeParams(pos) .nonEmpty) {
187
+ if (enumClass.typeParams .nonEmpty) {
156
188
val parent = interpolatedEnumParent(pos)
157
189
val impl = Template (emptyConstructor, parent :: Nil , EmptyValDef , Nil )
158
190
expandEnumModule(name, impl, mods, pos)
159
191
}
160
192
else {
161
- val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true )
162
- val prefix = if (simpleSeen) Nil else enumScaffolding
193
+ val (tag, scaffolding) = nextEnumTag(CaseKind .Simple )
163
194
val creator = Apply (Ident (nme.DOLLAR_NEW ), List (Literal (Constant (tag)), Literal (Constant (name.toString))))
164
195
val vdef = ValDef (name, enumClassRef, creator).withMods(mods | Final ).withPos(pos)
165
- flatTree(prefix ::: vdef :: Nil ).withPos(pos.startPos)
196
+ flatTree(scaffolding ::: vdef :: Nil ).withPos(pos.startPos)
166
197
}
167
198
}
0 commit comments