Skip to content

Commit f702773

Browse files
committed
Implementation of proposal changes
- rename utility methods - generate utility methods also for object cases
1 parent 9938344 commit f702773

File tree

5 files changed

+107
-50
lines changed

5 files changed

+107
-50
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ object desugar {
395395
.withMods(synthetic) :: Nil
396396
}
397397

398-
val enumTagMeths = if (isEnumCase) enumTagMeth :: Nil else Nil
398+
val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil
399399
copyMeths ::: enumTagMeths ::: productElemMeths.toList
400400
}
401401
else Nil

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

Lines changed: 75 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,26 @@ import collection.mutable.ListBuffer
1010
import util.Property
1111
import typer.ErrorReporting._
1212

13+
/** Helper methods to desugar enums */
1314
object DesugarEnums {
1415
import untpd._
1516
import desugar.DerivedFromParamTree
1617

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+
}
2121

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)]
2324

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
2927

28+
/** Is this an enum case that's situated in a companion object of an enum class? */
3029
def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean =
3130
tree.mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(tree)
3231

32+
/** Is enum case `tree` situated in a companion object of an enum class? */
3333
def enumCaseIsLegal(tree: Tree)(implicit ctx: Context): Boolean = (
3434
ctx.owner.is(ModuleClass) && enumClass.derivesFrom(defn.EnumClass)
3535
|| { ctx.error(em"case not allowed here, since owner ${ctx.owner} is not an `enum' object", tree.pos)
@@ -81,59 +81,89 @@ object DesugarEnums {
8181
TypeTree(enumClass.typeRef.appliedTo(targs)).withPos(pos)
8282
}
8383

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` */
8885
def enumClassRef(implicit ctx: Context) = TypeTree(enumClass.typeRef)
8986

87+
/** Add implied flags to an enum class or an enum case */
9088
def addEnumFlags(cdef: TypeDef)(implicit ctx: Context) =
9189
if (cdef.mods.hasMod[Mod.Enum]) cdef.withFlags(cdef.mods.flags | Abstract | Sealed)
9290
else if (isLegalEnumCase(cdef)) cdef.withFlags(cdef.mods.flags | Final)
9391
else cdef
9492

93+
private def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName)
94+
private def registerCall = Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil)
95+
9596
/** The following lists of definitions for an enum type E:
9697
*
9798
* 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+
*
102119
* private def $new(tag: Int, name: String) = new E {
103120
* def enumTag = tag
104121
* override def toString = name
105122
* $values.register(this)
106123
* }
107124
*/
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) = {
112126
def param(name: TermName, typ: Type) =
113127
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")
121128
val enumTagDef =
122129
DefDef(nme.enumTag, Nil, Nil, TypeTree(), Ident(nme.tag))
123130
val toStringDef =
124131
DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name))
125132
.withFlags(Override)
126-
val registerStat =
127-
Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil)
128133
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)
135164
}
136165

166+
/** Expand a module definition representing a parameterless enum case */
137167
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
138168
if (impl.parents.isEmpty)
139169
if (impl.body.isEmpty)
@@ -146,22 +176,23 @@ object DesugarEnums {
146176
def toStringMeth =
147177
DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString)))
148178
.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)
152183
}
153184

185+
/** Expand a simple enum case */
154186
def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
155-
if (reconstitutedEnumTypeParams(pos).nonEmpty) {
187+
if (enumClass.typeParams.nonEmpty) {
156188
val parent = interpolatedEnumParent(pos)
157189
val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil)
158190
expandEnumModule(name, impl, mods, pos)
159191
}
160192
else {
161-
val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true)
162-
val prefix = if (simpleSeen) Nil else enumScaffolding
193+
val (tag, scaffolding) = nextEnumTag(CaseKind.Simple)
163194
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
164195
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)
166197
}
167198
}

tests/run/enum-Color.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ enum Color {
44

55
object Test {
66
def main(args: Array[String]) =
7-
for (color <- Color.values) {
7+
for (color <- Color.enumValues) {
88
println(s"$color: ${color.enumTag}")
99
assert(Color.valueOf(color.enumTag) eq color)
1010
}

tests/run/enum-Option.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
enum class Option[+T] extends Serializable {
1+
enum class Option[+T >: Null] extends Serializable {
22
def isDefined: Boolean
33
}
44
object Option {
5-
def apply[T](x: T): Option[T] = if (x == null) None else Some(x)
5+
def apply[T >: Null](x: T): Option[T] = if (x == null) None else Some(x)
66
case Some(x: T) {
77
def isDefined = true
88
}
9-
case None extends Option[Nothing] {
9+
case None {
1010
def isDefined = false
1111
}
1212
}
1313

1414
object Test {
1515
def main(args: Array[String]) = {
1616
assert(Some(None).isDefined)
17-
Option(22) match { case Option.Some(x) => assert(x == 22) }
17+
Option("22") match { case Option.Some(x) => assert(x == "22") }
1818
}
1919
}

tests/run/planets.scala

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
enum class Planet(mass: Double, radius: Double) {
2+
private final val G = 6.67300E-11
3+
def surfaceGravity = G * mass / (radius * radius)
4+
def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
5+
}
6+
object Planet {
7+
case MERCURY extends Planet(3.303e+23, 2.4397e6)
8+
case VENUS extends Planet(4.869e+24, 6.0518e6)
9+
case EARTH extends Planet(5.976e+24, 6.37814e6)
10+
case MARS extends Planet(6.421e+23, 3.3972e6)
11+
case JUPITER extends Planet(1.9e+27, 7.1492e7)
12+
case SATURN extends Planet(5.688e+26, 6.0268e7)
13+
case URANUS extends Planet(8.686e+25, 2.5559e7)
14+
case NEPTUNE extends Planet(1.024e+26, 2.4746e7)
15+
}
16+
object Test {
17+
def main(args: Array[String]) = {
18+
import Planet._
19+
assert(enumValueNamed("SATURN") == SATURN)
20+
assert(enumValue(2) == EARTH)
21+
val earthWeight = args(0).toDouble
22+
val mass = earthWeight/EARTH.surfaceGravity
23+
for (p <- enumValues)
24+
println(s"Your weight on $p is ${p.surfaceWeight(mass)}")
25+
}
26+
}

0 commit comments

Comments
 (0)