aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2017-04-05 17:09:15 +0200
committerMartin Odersky <odersky@gmail.com>2017-04-05 17:09:22 +0200
commitf7027732a1d1e92f4d7525f2a984a24fdb7a0053 (patch)
treed1420d55db9013c536c7bba69ba03b8f9a4c2639
parent993834473c39b409bb2e21838a0e69fcc5fb4757 (diff)
downloaddotty-f7027732a1d1e92f4d7525f2a984a24fdb7a0053.tar.gz
dotty-f7027732a1d1e92f4d7525f2a984a24fdb7a0053.tar.bz2
dotty-f7027732a1d1e92f4d7525f2a984a24fdb7a0053.zip
Implementation of proposal changes
- rename utility methods - generate utility methods also for object cases
-rw-r--r--compiler/src/dotty/tools/dotc/ast/Desugar.scala2
-rw-r--r--compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala119
-rw-r--r--tests/run/enum-Color.scala2
-rw-r--r--tests/run/enum-Option.scala8
-rw-r--r--tests/run/planets.scala26
5 files changed, 107 insertions, 50 deletions
diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
index 80da75678..8499330fb 100644
--- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala
@@ -395,7 +395,7 @@ object desugar {
.withMods(synthetic) :: Nil
}
- val enumTagMeths = if (isEnumCase) enumTagMeth :: Nil else Nil
+ val enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil
copyMeths ::: enumTagMeths ::: productElemMeths.toList
}
else Nil
diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
index ae4ba23d5..e051824a1 100644
--- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
+++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
@@ -10,26 +10,26 @@ import collection.mutable.ListBuffer
import util.Property
import typer.ErrorReporting._
+/** Helper methods to desugar enums */
object DesugarEnums {
import untpd._
import desugar.DerivedFromParamTree
- /** Attachment containing: The number of enum cases seen so far, and whether a
- * simple enum case was already seen.
- */
- val EnumCaseCount = new Property.Key[(Int, Boolean)]
+ @sharable object CaseKind extends Enumeration {
+ val Simple, Object, Class = Value
+ }
- def enumClass(implicit ctx: Context) = ctx.owner.linkedClass
+ /** Attachment containing the number of enum cases and the smallest kind that was seen so far. */
+ val EnumCaseCount = new Property.Key[(Int, CaseKind.Value)]
- def nextEnumTag(isSimpleCase: Boolean)(implicit ctx: Context): (Int, Boolean) = {
- val (count, simpleSeen) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, false))
- ctx.tree.pushAttachment(EnumCaseCount, (count + 1, simpleSeen | isSimpleCase))
- (count, simpleSeen)
- }
+ /** the enumeration class that is a companion of the current object */
+ def enumClass(implicit ctx: Context) = ctx.owner.linkedClass
+ /** Is this an enum case that's situated in a companion object of an enum class? */
def isLegalEnumCase(tree: MemberDef)(implicit ctx: Context): Boolean =
tree.mods.hasMod[Mod.EnumCase] && enumCaseIsLegal(tree)
+ /** Is enum case `tree` situated in a companion object of an enum class? */
def enumCaseIsLegal(tree: Tree)(implicit ctx: Context): Boolean = (
ctx.owner.is(ModuleClass) && enumClass.derivesFrom(defn.EnumClass)
|| { ctx.error(em"case not allowed here, since owner ${ctx.owner} is not an `enum' object", tree.pos)
@@ -81,59 +81,89 @@ object DesugarEnums {
TypeTree(enumClass.typeRef.appliedTo(targs)).withPos(pos)
}
- def enumTagMeth(implicit ctx: Context) =
- DefDef(nme.enumTag, Nil, Nil, TypeTree(),
- Literal(Constant(nextEnumTag(isSimpleCase = false)._1)))
-
+ /** A type tree referring to `enumClass` */
def enumClassRef(implicit ctx: Context) = TypeTree(enumClass.typeRef)
+ /** Add implied flags to an enum class or an enum case */
def addEnumFlags(cdef: TypeDef)(implicit ctx: Context) =
if (cdef.mods.hasMod[Mod.Enum]) cdef.withFlags(cdef.mods.flags | Abstract | Sealed)
else if (isLegalEnumCase(cdef)) cdef.withFlags(cdef.mods.flags | Final)
else cdef
+ private def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName)
+ private def registerCall = Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil)
+
/** The following lists of definitions for an enum type E:
*
* private val $values = new EnumValues[E]
- * def valueOf = $values.fromInt
- * def withName = $values.fromName
- * def values = $values.values
- *
+ * def enumValue = $values.fromInt
+ * def enumValueNamed = $values.fromName
+ * def enumValues = $values.values
+ */
+ private def enumScaffolding(implicit ctx: Context): List[Tree] = {
+ val enumType = enumClass.typeRef.appliedTo(enumClass.typeParams.map(_ => TypeBounds.empty))
+ def enumDefDef(name: String, select: String) =
+ DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select))
+ val privateValuesDef =
+ ValDef(nme.DOLLAR_VALUES, TypeTree(),
+ New(TypeTree(defn.EnumValuesType.appliedTo(enumType :: Nil)), ListOfNil))
+ .withFlags(Private)
+ val valueOfDef = enumDefDef("enumValue", "fromInt")
+ val withNameDef = enumDefDef("enumValueNamed", "fromName")
+ val valuesDef = enumDefDef("enumValues", "values")
+ List(privateValuesDef, valueOfDef, withNameDef, valuesDef)
+ }
+
+ /** A creation method for a value of enum type `E`, which is defined as follows:
+ *
* private def $new(tag: Int, name: String) = new E {
* def enumTag = tag
* override def toString = name
* $values.register(this)
* }
*/
- private def enumScaffolding(implicit ctx: Context): List[Tree] = {
- def valuesDot(name: String) = Select(Ident(nme.DOLLAR_VALUES), name.toTermName)
- def enumDefDef(name: String, select: String) =
- DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select))
+ private def enumValueCreator(implicit ctx: Context) = {
def param(name: TermName, typ: Type) =
ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param)
- val privateValuesDef =
- ValDef(nme.DOLLAR_VALUES, TypeTree(),
- New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil))
- .withFlags(Private)
- val valueOfDef = enumDefDef("valueOf", "fromInt")
- val withNameDef = enumDefDef("withName", "fromName")
- val valuesDef = enumDefDef("values", "values")
val enumTagDef =
DefDef(nme.enumTag, Nil, Nil, TypeTree(), Ident(nme.tag))
val toStringDef =
DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name))
.withFlags(Override)
- val registerStat =
- Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil)
def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef,
- List(enumTagDef, toStringDef, registerStat)))
- val newDef =
- DefDef(nme.DOLLAR_NEW, Nil,
- List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))),
- TypeTree(), creator)
- List(privateValuesDef, valueOfDef, withNameDef, valuesDef, newDef)
+ List(enumTagDef, toStringDef, registerCall)))
+ DefDef(nme.DOLLAR_NEW, Nil,
+ List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))),
+ TypeTree(), creator)
+ }
+
+ /** A pair consisting of
+ * - the next enum tag
+ * - scaffolding containing the necessary definitions for singleton enum cases
+ * unless that scaffolding was already generated by a previous call to `nextEnumKind`.
+ */
+ def nextEnumTag(kind: CaseKind.Value)(implicit ctx: Context): (Int, List[Tree]) = {
+ val (count, seenKind) = ctx.tree.removeAttachment(EnumCaseCount).getOrElse((0, CaseKind.Class))
+ val minKind = if (kind < seenKind) kind else seenKind
+ ctx.tree.pushAttachment(EnumCaseCount, (count + 1, minKind))
+ val scaffolding =
+ if (kind >= seenKind) Nil
+ else if (kind == CaseKind.Object) enumScaffolding
+ else if (seenKind == CaseKind.Object) enumValueCreator :: Nil
+ else enumScaffolding :+ enumValueCreator
+ (count, scaffolding)
+ }
+
+ /** A pair consisting of
+ * - a method returning the next enum tag
+ * - scaffolding as defined in `nextEnumTag`
+ */
+ def enumTagMeth(kind: CaseKind.Value)(implicit ctx: Context): (DefDef, List[Tree]) = {
+ val (tag, scaffolding) = nextEnumTag(kind)
+ (DefDef(nme.enumTag, Nil, Nil, TypeTree(), Literal(Constant(tag))), scaffolding)
}
+ /** Expand a module definition representing a parameterless enum case */
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
if (impl.parents.isEmpty)
if (impl.body.isEmpty)
@@ -146,22 +176,23 @@ object DesugarEnums {
def toStringMeth =
DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString)))
.withFlags(Override)
- val impl1 = cpy.Template(impl)(body =
- impl.body ++ List(enumTagMeth, toStringMeth))
- ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos)
+ val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object)
+ val impl1 = cpy.Template(impl)(body = impl.body ++ List(tagMeth, toStringMeth, registerCall))
+ val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final).withPos(pos)
+ flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos)
}
+ /** Expand a simple enum case */
def expandSimpleEnumCase(name: TermName, mods: Modifiers, pos: Position)(implicit ctx: Context): Tree =
- if (reconstitutedEnumTypeParams(pos).nonEmpty) {
+ if (enumClass.typeParams.nonEmpty) {
val parent = interpolatedEnumParent(pos)
val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil)
expandEnumModule(name, impl, mods, pos)
}
else {
- val (tag, simpleSeen) = nextEnumTag(isSimpleCase = true)
- val prefix = if (simpleSeen) Nil else enumScaffolding
+ val (tag, scaffolding) = nextEnumTag(CaseKind.Simple)
val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString))))
val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final).withPos(pos)
- flatTree(prefix ::: vdef :: Nil).withPos(pos.startPos)
+ flatTree(scaffolding ::: vdef :: Nil).withPos(pos.startPos)
}
}
diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala
index 1a077bf8e..836e02c62 100644
--- a/tests/run/enum-Color.scala
+++ b/tests/run/enum-Color.scala
@@ -4,7 +4,7 @@ enum Color {
object Test {
def main(args: Array[String]) =
- for (color <- Color.values) {
+ for (color <- Color.enumValues) {
println(s"$color: ${color.enumTag}")
assert(Color.valueOf(color.enumTag) eq color)
}
diff --git a/tests/run/enum-Option.scala b/tests/run/enum-Option.scala
index b9efadf0d..76f5641b3 100644
--- a/tests/run/enum-Option.scala
+++ b/tests/run/enum-Option.scala
@@ -1,12 +1,12 @@
-enum class Option[+T] extends Serializable {
+enum class Option[+T >: Null] extends Serializable {
def isDefined: Boolean
}
object Option {
- def apply[T](x: T): Option[T] = if (x == null) None else Some(x)
+ def apply[T >: Null](x: T): Option[T] = if (x == null) None else Some(x)
case Some(x: T) {
def isDefined = true
}
- case None extends Option[Nothing] {
+ case None {
def isDefined = false
}
}
@@ -14,6 +14,6 @@ object Option {
object Test {
def main(args: Array[String]) = {
assert(Some(None).isDefined)
- Option(22) match { case Option.Some(x) => assert(x == 22) }
+ Option("22") match { case Option.Some(x) => assert(x == "22") }
}
}
diff --git a/tests/run/planets.scala b/tests/run/planets.scala
new file mode 100644
index 000000000..2fff01edc
--- /dev/null
+++ b/tests/run/planets.scala
@@ -0,0 +1,26 @@
+enum class Planet(mass: Double, radius: Double) {
+ private final val G = 6.67300E-11
+ def surfaceGravity = G * mass / (radius * radius)
+ def surfaceWeight(otherMass: Double) = otherMass * surfaceGravity
+}
+object Planet {
+ case MERCURY extends Planet(3.303e+23, 2.4397e6)
+ case VENUS extends Planet(4.869e+24, 6.0518e6)
+ case EARTH extends Planet(5.976e+24, 6.37814e6)
+ case MARS extends Planet(6.421e+23, 3.3972e6)
+ case JUPITER extends Planet(1.9e+27, 7.1492e7)
+ case SATURN extends Planet(5.688e+26, 6.0268e7)
+ case URANUS extends Planet(8.686e+25, 2.5559e7)
+ case NEPTUNE extends Planet(1.024e+26, 2.4746e7)
+}
+object Test {
+ def main(args: Array[String]) = {
+ import Planet._
+ assert(enumValueNamed("SATURN") == SATURN)
+ assert(enumValue(2) == EARTH)
+ val earthWeight = args(0).toDouble
+ val mass = earthWeight/EARTH.surfaceGravity
+ for (p <- enumValues)
+ println(s"Your weight on $p is ${p.surfaceWeight(mass)}")
+ }
+}