From c9982edd55a488ac2e65438cd2e6edb43cf430d0 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Tue, 4 Jun 2019 15:55:38 +0200 Subject: [PATCH 01/20] Define ordinal and name for enums --- .../src/dotty/tools/dotc/ast/Desugar.scala | 8 ++- .../dotty/tools/dotc/ast/DesugarEnums.scala | 54 ++++++++++--------- .../src/dotty/tools/dotc/core/StdNames.scala | 2 +- library/src/scala/Enum.scala | 5 +- library/src/scala/runtime/EnumValues.scala | 4 +- 5 files changed, 43 insertions(+), 30 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index daf9aa7e5c3b..15f2f9dc5d00 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -545,7 +545,11 @@ object desugar { yield syntheticProperty(nme.selectorName(i), caseParams(i).tpt, Select(This(EmptyTypeIdent), caseParams(i).name)) } - def enumTagMeths = if (isEnumCase) enumTagMeth(CaseKind.Class)._1 :: Nil else Nil + def ordinalMeths = + if (isEnumCase) + ordinalMethLit(nextOrdinal(CaseKind.Class)._1) :: + nameMethLit(className.toString) :: Nil + else Nil def copyMeths = { val hasRepeatedParam = constrVparamss.exists(_.exists { case ValDef(_, tpt, _) => isRepeated(tpt) @@ -582,7 +586,7 @@ object desugar { } if (isCaseClass) - productElemNameMeth :: copyMeths ::: enumTagMeths ::: productElemMeths + productElemNameMeth :: copyMeths ::: ordinalMeths ::: productElemMeths else Nil } diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 2fcf7a107fc7..5e943400a311 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -102,8 +102,9 @@ object DesugarEnums { /** 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 + * private def $new(tag: Int, name_: String) = new E { + * override def ordinal = tag + * override def name = name_ * override def toString = name * $values.register(this) * } @@ -111,20 +112,17 @@ object DesugarEnums { private def enumValueCreator(implicit ctx: Context) = { def param(name: TermName, typ: Type) = ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) - 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 ordinalDef = ordinalMeth(Ident(nme.tag)) + val nameDef = nameMeth(Ident(nme.name_)) val creator = New(Template( constr = emptyConstructor, parents = enumClassRef :: Nil, derived = Nil, self = EmptyValDef, - body = List(enumTagDef, toStringDef) ++ registerCall + body = List(ordinalDef, nameDef, toStringMethAsName) ++ registerCall ).withAttachment(ExtendsSingletonMirror, ())) DefDef(nme.DOLLAR_NEW, Nil, - List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), + List(List(param(nme.tag, defn.IntType), param(nme.name_, defn.StringType))), TypeTree(), creator).withFlags(Private | Synthetic) } @@ -232,7 +230,7 @@ object DesugarEnums { * - 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]) = { + def nextOrdinal(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)) @@ -244,14 +242,23 @@ object DesugarEnums { (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) - } + def ordinalMeth(body: Tree)(implicit ctx: Context): DefDef = + DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withFlags(Override) + + def nameMeth(body: Tree)(implicit ctx: Context): DefDef = + DefDef(nme.name, Nil, Nil, TypeTree(defn.StringType), body).withFlags(Override) + + def toStringMeth(body: Tree)(implicit ctx: Context): DefDef = + DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), body).withFlags(Override) + + def ordinalMethLit(ord: Int)(implicit ctx: Context): DefDef = + ordinalMeth(Literal(Constant(ord))) + + def nameMethLit(name: String)(implicit ctx: Context): DefDef = + nameMeth(Literal(Constant(name))) + + def toStringMethAsName(implicit ctx: Context): DefDef = + toStringMeth(Ident(nme.name)) /** Expand a module definition representing a parameterless enum case */ def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, span: Span)(implicit ctx: Context): Tree = { @@ -260,11 +267,10 @@ object DesugarEnums { else if (impl.parents.isEmpty) expandSimpleEnumCase(name, mods, span) else { - def toStringMeth = - DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), Literal(Constant(name.toString))) - .withFlags(Override) - val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) - val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall) + val (tag, scaffolding) = nextOrdinal(CaseKind.Object) + val ordinalDef = ordinalMethLit(tag) + val nameDef = nameMethLit(name.toString) + val impl1 = cpy.Template(impl)(body = List(ordinalDef, nameDef, toStringMethAsName) ++ registerCall) .withAttachment(ExtendsSingletonMirror, ()) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) @@ -280,7 +286,7 @@ object DesugarEnums { expandEnumModule(name, impl, mods, span) } else { - val (tag, scaffolding) = nextEnumTag(CaseKind.Simple) + val (tag, scaffolding) = nextOrdinal(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) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index d0f359d1d9b8..ab269f9200e8 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -420,7 +420,6 @@ object StdNames { val elems: N = "elems" val emptyValDef: N = "emptyValDef" val ensureAccessible : N = "ensureAccessible" - val enumTag: N = "enumTag" val eq: N = "eq" val eqInstance: N = "eqInstance" val equalsNumChar : N = "equalsNumChar" @@ -485,6 +484,7 @@ object StdNames { val mirror : N = "mirror" val moduleClass : N = "moduleClass" val name: N = "name" + val name_ : N = "name_" val ne: N = "ne" val newFreeTerm: N = "newFreeTerm" val newFreeType: N = "newFreeType" diff --git a/library/src/scala/Enum.scala b/library/src/scala/Enum.scala index 7d2eefb3df9c..311d491305b7 100644 --- a/library/src/scala/Enum.scala +++ b/library/src/scala/Enum.scala @@ -4,5 +4,8 @@ package scala trait Enum { /** A number uniquely identifying a case of an enum */ - def enumTag: Int + def ordinal: Int + + /** Name of the enum's case */ + def name: String } diff --git a/library/src/scala/runtime/EnumValues.scala b/library/src/scala/runtime/EnumValues.scala index 64901512cad1..3f59faed5d56 100644 --- a/library/src/scala/runtime/EnumValues.scala +++ b/library/src/scala/runtime/EnumValues.scala @@ -7,8 +7,8 @@ class EnumValues[E <: Enum] { private[this] var fromNameCache: Map[String, E] = null def register(v: E) = { - require(!myMap.contains(v.enumTag)) - myMap = myMap.updated(v.enumTag, v) + require(!myMap.contains(v.ordinal)) + myMap = myMap.updated(v.ordinal, v) fromNameCache = null } From 234383080d19876aa77b03147b4f328ad8ae7d87 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Tue, 4 Jun 2019 16:40:17 +0200 Subject: [PATCH 02/20] Module methods for enums implemented --- .../dotty/tools/dotc/ast/DesugarEnums.scala | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 5e943400a311..aa5cd4d1297c 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -74,8 +74,12 @@ object DesugarEnums { else if (isEnumCase(cdef)) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Final)) else cdef + private def valuesDotTerm(name: TermName)(implicit src: SourceFile) = + Select(Ident(nme.DOLLAR_VALUES), name) + private def valuesDot(name: String)(implicit src: SourceFile) = - Select(Ident(nme.DOLLAR_VALUES), name.toTermName) + valuesDotTerm(name.toTermName) + private def registerCall(implicit ctx: Context): List[Tree] = if (enumClass.typeParams.nonEmpty) Nil else Apply(valuesDot("register"), This(EmptyTypeIdent) :: Nil) :: Nil @@ -83,21 +87,22 @@ object DesugarEnums { /** The following lists of definitions for an enum type E: * * private val $values = new EnumValues[E] - * def enumValue = $values.fromInt - * def enumValueNamed = $values.fromName - * def enumValues = $values.values + * def valueOf = $values.fromName + * def values = $values.values.toArray */ private def enumScaffolding(implicit ctx: Context): List[Tree] = { - def enumDefDef(name: String, select: String) = - DefDef(name.toTermName, Nil, Nil, TypeTree(), valuesDot(select)) + val valuesDef = + DefDef(nme.values, Nil, Nil, TypeTree(), Select(valuesDotTerm(nme.values), nme.toArray)) val privateValuesDef = ValDef(nme.DOLLAR_VALUES, TypeTree(), New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil)) - .withFlags(Private) - val valueOfDef = enumDefDef("enumValue", "fromInt") - val withNameDef = enumDefDef("enumValueNamed", "fromName") - val valuesDef = enumDefDef("enumValues", "values") - List(privateValuesDef, valueOfDef, withNameDef, valuesDef) + .withFlags(Private) + val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.name, defn.StringType) :: Nil), + TypeTree(), Apply(valuesDot("fromName"), Ident(nme.name) :: Nil)) + + valuesDef :: + privateValuesDef :: + valueOfDef :: Nil } /** A creation method for a value of enum type `E`, which is defined as follows: @@ -110,8 +115,6 @@ object DesugarEnums { * } */ private def enumValueCreator(implicit ctx: Context) = { - def param(name: TermName, typ: Type) = - ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) val ordinalDef = ordinalMeth(Ident(nme.tag)) val nameDef = nameMeth(Ident(nme.name_)) val creator = New(Template( @@ -242,6 +245,9 @@ object DesugarEnums { (count, scaffolding) } + def param(name: TermName, typ: Type)(implicit ctx: Context) = + ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) + def ordinalMeth(body: Tree)(implicit ctx: Context): DefDef = DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withFlags(Override) From 231b51f50cde0d3781d4585f48f545c464f2b697 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Tue, 4 Jun 2019 16:45:57 +0200 Subject: [PATCH 03/20] Conform tests to the new Enums API --- tests/neg/i5008.scala | 2 +- tests/neg/i5019.scala | 2 +- tests/patmat/planets.scala | 5 ++--- tests/pos-with-compiler/tasty/definitions.scala | 16 ++++++++-------- tests/pos/enum-List-control.scala | 6 ++++-- tests/pos/reference/enums.scala | 2 +- tests/run/enum-Color.check | 6 +++--- tests/run/enum-Color.scala | 6 +++--- tests/run/generic/Color.scala | 4 ++-- tests/run/generic/Enum.scala | 6 +++--- tests/run/generic/List.scala | 8 ++++---- tests/run/generic/SearchResult.scala | 8 ++++---- tests/run/generic/Tree.scala | 14 +++++++------- tests/run/i4961.scala | 5 +---- tests/run/planets.scala | 5 ++--- 15 files changed, 46 insertions(+), 49 deletions(-) diff --git a/tests/neg/i5008.scala b/tests/neg/i5008.scala index 25e19f423982..c70f2e291eaa 100644 --- a/tests/neg/i5008.scala +++ b/tests/neg/i5008.scala @@ -5,4 +5,4 @@ enum Baz extends Foo { case Z } // error enum Quux extends Foo with Bar { case Z } // error class Quuw extends Foo // error -class Quuz extends Foo { val enumTag = 1 } // error +class Quuz extends Foo { val ordinal = 1 } // error diff --git a/tests/neg/i5019.scala b/tests/neg/i5019.scala index 0651aac8edac..ecb852bf052a 100644 --- a/tests/neg/i5019.scala +++ b/tests/neg/i5019.scala @@ -1,3 +1,3 @@ enum A { // error - def newA(tag: Int) = new A { val enumTag = tag } // error + def newA(tag: Int) = new A { val ordinal = tag } // error } diff --git a/tests/patmat/planets.scala b/tests/patmat/planets.scala index 22c731428f70..2c4f31b17ca3 100644 --- a/tests/patmat/planets.scala +++ b/tests/patmat/planets.scala @@ -15,11 +15,10 @@ enum Planet(mass: Double, radius: Double) { object Test { def main(args: Array[String]) = { import Planet._ - assert(enumValueNamed("SATURN") == SATURN) - assert(enumValue(2) == EARTH) + assert(valueOf("SATURN") == SATURN) val earthWeight = 100 val mass = earthWeight/EARTH.surfaceGravity - for (p <- enumValues) + for (p <- values) println(s"Your weight on $p is ${p.surfaceWeight(mass)}") } } diff --git a/tests/pos-with-compiler/tasty/definitions.scala b/tests/pos-with-compiler/tasty/definitions.scala index 07e822cd851b..85b6e4855624 100644 --- a/tests/pos-with-compiler/tasty/definitions.scala +++ b/tests/pos-with-compiler/tasty/definitions.scala @@ -65,13 +65,13 @@ object definitions { /** Trees denoting terms */ enum Term extends Statement { def tpe: Type = ??? - case Ident(name: String, override val tpe: Type) - case Select(prefix: Term, name: String, signature: Option[Signature]) + case Ident(nme: String, override val tpe: Type) + case Select(prefix: Term, nme: String, signature: Option[Signature]) case Literal(value: Constant) case This(id: Option[Id]) case New(tpt: TypeTree) case Throw(expr: Term) - case NamedArg(name: String, arg: Term) + case NamedArg(nme: String, arg: Term) case Apply(fn: Term, args: List[Term]) case TypeApply(fn: Term, args: List[TypeTree]) case Super(thiz: Term, mixin: Option[Id]) @@ -94,9 +94,9 @@ object definitions { enum TypeTree extends Positioned { def tpe: Type = ??? case Synthetic() - case Ident(name: String, override val tpe: Type) - case TermSelect(prefix: Term, name: String) - case TypeSelect(prefix: TypeTree, name: String) + case Ident(nme: String, override val tpe: Type) + case TermSelect(prefix: Term, nme: String) + case TypeSelect(prefix: TypeTree, nme: String) case Singleton(ref: Term) case Refined(underlying: TypeTree, refinements: List[Definition]) case Applied(tycon: TypeTree, args: List[TypeTree | TypeBoundsTree]) @@ -105,7 +105,7 @@ object definitions { case Or(left: TypeTree, right: TypeTree) case ByName(tpt: TypeTree) case TypeLambda(tparams: List[TypeDef], body: Type | TypeBoundsTree) - case Bind(name: String, bounds: TypeBoundsTree) + case Bind(nme: String, bounds: TypeBoundsTree) } /** Trees denoting type bounds */ @@ -122,7 +122,7 @@ object definitions { enum Pattern extends Positioned { def tpe: Type = ??? case Value(v: Term) - case Bind(name: String, pat: Pattern) + case Bind(nme: String, pat: Pattern) case Unapply(unapply: Term, implicits: List[Term], pats: List[Pattern]) case Alternative(pats: List[Pattern]) case TypeTest(tpt: TypeTree) diff --git a/tests/pos/enum-List-control.scala b/tests/pos/enum-List-control.scala index b269df43b242..f662e254b543 100644 --- a/tests/pos/enum-List-control.scala +++ b/tests/pos/enum-List-control.scala @@ -1,13 +1,15 @@ abstract sealed class List[T] extends Enum object List { final class Cons[T](x: T, xs: List[T]) extends List[T] { - def enumTag = 0 + def ordinal = 0 + def name = "Cons" } object Cons { def apply[T](x: T, xs: List[T]): List[T] = new Cons(x, xs) } final class Nil[T]() extends List[T] { - def enumTag = 1 + def ordinal = 1 + def name = "Nil" } object Nil { def apply[T](): List[T] = new Nil() diff --git a/tests/pos/reference/enums.scala b/tests/pos/reference/enums.scala index 71179c7aed61..fa684d735aeb 100644 --- a/tests/pos/reference/enums.scala +++ b/tests/pos/reference/enums.scala @@ -47,7 +47,7 @@ object Planet { def main(args: Array[String]) = { val earthWeight = args(0).toDouble val mass = earthWeight/EARTH.surfaceGravity - for (p <- enumValues) + for (p <- values) println(s"Your weight on $p is ${p.surfaceWeight(mass)}") } } diff --git a/tests/run/enum-Color.check b/tests/run/enum-Color.check index 865c47e495d1..43edee1968a7 100644 --- a/tests/run/enum-Color.check +++ b/tests/run/enum-Color.check @@ -1,3 +1,3 @@ -Red: 0 -Green: 1 -Blue: 2 +Red: Red +Green: Green +Blue: Blue diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala index 31b850329407..f48663b0005b 100644 --- a/tests/run/enum-Color.scala +++ b/tests/run/enum-Color.scala @@ -5,9 +5,9 @@ enum Color { object Test { def main(args: Array[String]) = - for (color <- Color.enumValues) { - println(s"$color: ${color.enumTag}") - assert(Color.enumValue(color.enumTag) eq color) + for (color <- Color.values) { + println(s"$color: ${color.name}") + assert(Color.valueOf(color.name) eq color) import Color._ color match { case Red | Green | Blue => diff --git a/tests/run/generic/Color.scala b/tests/run/generic/Color.scala index 7f2a8818c0fc..3db940cdc94a 100644 --- a/tests/run/generic/Color.scala +++ b/tests/run/generic/Color.scala @@ -18,7 +18,7 @@ object Color { def values = $values.values private def $new(tag: Int, name: String) = new Color { - def enumTag = tag + def ordinal = tag override def toString = name $values.register(this) } @@ -29,7 +29,7 @@ object Color { implicit val ColorShape: Color `shaped` EnumValue[Color] = new (Color `shaped` EnumValue[Color]) { - def toShape(x: Color) = EnumValue(x.enumTag) + def toShape(x: Color) = EnumValue(x.ordinal) def fromShape(x: EnumValue[Color]) = Color.valueOf(x.tag) } } diff --git a/tests/run/generic/Enum.scala b/tests/run/generic/Enum.scala index ce64ac054933..3e04786de07d 100644 --- a/tests/run/generic/Enum.scala +++ b/tests/run/generic/Enum.scala @@ -1,7 +1,7 @@ package generic trait Enum { - def enumTag: Int + def ordinal: Int } object runtime { @@ -10,8 +10,8 @@ object runtime { private[this] var fromNameCache: Map[String, E] = null def register(v: E) = { - require(!myMap.contains(v.enumTag)) - myMap = myMap.updated(v.enumTag, v) + require(!myMap.contains(v.ordinal)) + myMap = myMap.updated(v.ordinal, v) fromNameCache = null } diff --git a/tests/run/generic/List.scala b/tests/run/generic/List.scala index bc01ce63fc14..07ed98295ba3 100644 --- a/tests/run/generic/List.scala +++ b/tests/run/generic/List.scala @@ -10,7 +10,7 @@ import Shapes._ sealed trait List0[T] extends Enum object List0 { abstract case class Cons[T](hd: T, tl: List0[T]) extends List0[T] { - def enumTag = 0 + def ordinal = 0 } object Cons { def apply[T](x: T, xs: List0[T]): List0[T] = new Cons(x, xs) {} @@ -22,7 +22,7 @@ object List0 { } abstract case class Nil[T]() extends List0[T] { - def enumTag = 1 + def ordinal = 1 } object Nil { def apply[T](): List0[T] = new Nil[T]() {} @@ -54,7 +54,7 @@ object List0 { sealed trait List[+T] extends Enum object List { abstract case class Cons[T](hd: T, tl: List[T]) extends List[T] { - def enumTag = 0 + def ordinal = 0 } object Cons { def apply[T](x: T, xs: List[T]): List[T] = new Cons(x, xs) {} @@ -67,7 +67,7 @@ object List { } val Nil = new List[Nothing] { - def enumTag = 1 + def ordinal = 1 override def toString = "Nil" } diff --git a/tests/run/generic/SearchResult.scala b/tests/run/generic/SearchResult.scala index 96ec7e3f2536..bc1b4a0dd901 100644 --- a/tests/run/generic/SearchResult.scala +++ b/tests/run/generic/SearchResult.scala @@ -19,13 +19,13 @@ object SearchResult { def values = $values.values private def $new(tag: Int, name: String) = new SearchResult { - def enumTag = tag + def ordinal = tag override def toString = name $values.register(this) } abstract case class Success(result: Color) extends SearchResult { - def enumTag = 0 + def ordinal = 0 } object Success { def apply(result: Color): SearchResult = new Success(result) {} @@ -40,7 +40,7 @@ object SearchResult { val NoMatch: SearchResult = $new(2, "NoMatch") abstract case class Ambiguous(alt1: SearchResult, alt2: SearchResult) extends SearchResult { - def enumTag = 3 + def ordinal = 3 } object Ambiguous { def apply(alt1: SearchResult, alt2: SearchResult): SearchResult = new Ambiguous(alt1, alt2) {} @@ -58,7 +58,7 @@ object SearchResult { def toShape(x: SearchResult) = x match { case x: Success => Fst(x) case x: Ambiguous => Snd(Fst(x)) - case x => Snd(Snd(EnumValue(x.enumTag))) + case x => Snd(Snd(EnumValue(x.ordinal))) } def fromShape(x: Sum[Success, Sum[Ambiguous, EnumValue[SearchResult]]]): SearchResult = x match { case Fst(s) => s diff --git a/tests/run/generic/Tree.scala b/tests/run/generic/Tree.scala index 673506b07a0b..32996685fc90 100644 --- a/tests/run/generic/Tree.scala +++ b/tests/run/generic/Tree.scala @@ -17,25 +17,25 @@ sealed trait Tree[TR] extends Enum object Tree { val True: Tree[Boolean] = new Tree[Boolean] { - def enumTag = 0 + def ordinal = 0 override def toString = "True" } implicit def TrueSingleton: Singleton[True.type] = new Singleton[True.type](True) val False: Tree[Boolean] = new Tree[Boolean] { - def enumTag = 1 + def ordinal = 1 override def toString = "False" } implicit def FalseSingleton: Singleton[False.type] = new Singleton[False.type](False) val Zero: Tree[Int] = new Tree[Int] { - def enumTag = 2 + def ordinal = 2 override def toString = "Zero" } implicit def ZeroSingleton: Singleton[Zero.type] = new Singleton[Zero.type](Zero) abstract case class Succ(n: Tree[Int]) extends Tree[Int] { - def enumTag = 3 + def ordinal = 3 } object Succ { def apply(x: Tree[Int]): Tree[Int] = new Succ(x) {} @@ -46,7 +46,7 @@ object Tree { } abstract case class Pred(n: Tree[Int]) extends Tree[Int] { - def enumTag = 4 + def ordinal = 4 } object Pred { def apply(x: Tree[Int]): Tree[Int] = new Pred(x) {} @@ -57,7 +57,7 @@ object Tree { } abstract case class IsZero(n: Tree[Int]) extends Tree[Boolean] { - def enumTag = 5 + def ordinal = 5 } object IsZero { def apply(x: Tree[Int]): Tree[Boolean] = new IsZero(x) {} @@ -68,7 +68,7 @@ object Tree { } abstract case class If[T](cond: Tree[Boolean], thenp: Tree[T], elsep: Tree[T]) extends Tree[T] { - def enumTag = 6 + def ordinal = 6 } object If { def apply[T](cond: Tree[Boolean], thenp: Tree[T], elsep: Tree[T]): Tree[T] = new If(cond, thenp, elsep) {} diff --git a/tests/run/i4961.scala b/tests/run/i4961.scala index c89469c0c3df..c49056e88925 100644 --- a/tests/run/i4961.scala +++ b/tests/run/i4961.scala @@ -1,7 +1,6 @@ trait Animal object Animal { - def enumValues: Iterable[Animal] = Dog.enumValues ++ Cat.enumValues - def enumValueNamed = Dog.enumValueNamed ++ Cat.enumValueNamed + def enumValues: Iterable[Animal] = Dog.values ++ Cat.values } enum Dog extends Animal { @@ -27,7 +26,5 @@ object Test { ) assert(Animal.enumValues == values) - assert(Animal.enumValueNamed("Boxer") == Boxer) - assert(Animal.enumValueNamed("Ragdoll") == Ragdoll) } } diff --git a/tests/run/planets.scala b/tests/run/planets.scala index 22c731428f70..2c4f31b17ca3 100644 --- a/tests/run/planets.scala +++ b/tests/run/planets.scala @@ -15,11 +15,10 @@ enum Planet(mass: Double, radius: Double) { object Test { def main(args: Array[String]) = { import Planet._ - assert(enumValueNamed("SATURN") == SATURN) - assert(enumValue(2) == EARTH) + assert(valueOf("SATURN") == SATURN) val earthWeight = 100 val mass = earthWeight/EARTH.surfaceGravity - for (p <- enumValues) + for (p <- values) println(s"Your weight on $p is ${p.surfaceWeight(mass)}") } } From 56a76a5ca779756a59aaf0d89db1ede8f2e8ac3e Mon Sep 17 00:00:00 2001 From: Anatolii Date: Tue, 4 Jun 2019 17:59:12 +0200 Subject: [PATCH 04/20] Enums valuesOf exception type conforms to that of Java enums --- .../dotty/tools/dotc/ast/DesugarEnums.scala | 24 ++++++++++++++++--- .../dotty/tools/dotc/core/Definitions.scala | 5 ++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index aa5cd4d1297c..bd7c131903bf 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -87,7 +87,12 @@ object DesugarEnums { /** The following lists of definitions for an enum type E: * * private val $values = new EnumValues[E] - * def valueOf = $values.fromName + * def valueOf(name: String) = + * try $values.fromName(name) catch + * { + * case ex$:NoSuchElementException => + * throw new IllegalArgumentException("key not found: ".concat(name)) + * } * def values = $values.values.toArray */ private def enumScaffolding(implicit ctx: Context): List[Tree] = { @@ -96,9 +101,22 @@ object DesugarEnums { val privateValuesDef = ValDef(nme.DOLLAR_VALUES, TypeTree(), New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil)) - .withFlags(Private) + .withFlags(Private) + + val valuesOfExnMessage = Apply( + Select(Literal(Constant("key not found: ")), "concat".toTermName) + , Ident(nme.name) :: Nil) + val valuesOfBody = Try( + expr = Apply(valuesDot("fromName"), Ident(nme.name) :: Nil) + , cases = CaseDef( + pat = Typed(Ident(nme.DEFAULT_EXCEPTION_NAME), TypeTree(defn.NoSuchElementExceptionType)) + , guard = EmptyTree + , body = Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(valuesOfExnMessage :: Nil))) + ) :: Nil + , finalizer = EmptyTree + ) val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.name, defn.StringType) :: Nil), - TypeTree(), Apply(valuesDot("fromName"), Ident(nme.name) :: Nil)) + TypeTree(), valuesOfBody) valuesDef :: privateValuesDef :: diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b074193c2c05..bb2fbc8148fd 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -605,6 +605,11 @@ class Definitions { @threadUnsafe lazy val SystemClass: ClassSymbol = ctx.requiredClass("java.lang.System") @threadUnsafe lazy val SystemModule: Symbol = SystemClass.linkedClass + lazy val NoSuchElementExceptionClass = ctx.requiredClass("java.util.NoSuchElementException") + def NoSuchElementExceptionType = NoSuchElementExceptionClass.typeRef + lazy val IllegalArgumentExceptionClass = ctx.requiredClass("java.lang.IllegalArgumentException") + def IllegalArgumentExceptionType = IllegalArgumentExceptionClass.typeRef + // in scalac modified to have Any as parent @threadUnsafe lazy val ThrowableType: TypeRef = ctx.requiredClassRef("java.lang.Throwable") From 023778c5d5658d92394218fe35e15e776b038a37 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Tue, 4 Jun 2019 17:59:29 +0200 Subject: [PATCH 05/20] Tests added for enum java compat API --- tests/run/enums-java-compat.check | 10 ++++++++++ tests/run/enums-java-compat/Enums.scala | 22 ++++++++++++++++++++++ tests/run/enums-java-compat/Test.scala | 19 +++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 tests/run/enums-java-compat.check create mode 100644 tests/run/enums-java-compat/Enums.scala create mode 100644 tests/run/enums-java-compat/Test.scala diff --git a/tests/run/enums-java-compat.check b/tests/run/enums-java-compat.check new file mode 100644 index 000000000000..2005baaec107 --- /dev/null +++ b/tests/run/enums-java-compat.check @@ -0,0 +1,10 @@ +name: EARTH +ordinal: 0 +toString: EARTH +Values class: class [LA; +MONDAY : 0 +TUESDAY : 1 +SATURDAY : 2 +Stuff : 3 +By-name value: MONDAY +Correctly failed to retrieve illegal name, message: key not found: stuff diff --git a/tests/run/enums-java-compat/Enums.scala b/tests/run/enums-java-compat/Enums.scala new file mode 100644 index 000000000000..1b7d541c99d8 --- /dev/null +++ b/tests/run/enums-java-compat/Enums.scala @@ -0,0 +1,22 @@ +class JEnum { + def name: String = "Foo" + def ordinal: Int = 10 + def action = "fofofo" +} + +enum A extends JEnum { + case MONDAY, TUESDAY, SATURDAY + case Stuff + case Someday(x: String) + def report = "Reported" +} + +trait Foo1 +trait Bar + +enum B(val gravity: Double)(val isItGood: Boolean) extends Foo1 { + case EARTH extends B(9.8)(true) + case JUPITER extends B(100)(true) + case MOON extends B(4.3)(true) + case Foo extends B(10)(true) with Bar +} diff --git a/tests/run/enums-java-compat/Test.scala b/tests/run/enums-java-compat/Test.scala new file mode 100644 index 000000000000..d29429b70f2e --- /dev/null +++ b/tests/run/enums-java-compat/Test.scala @@ -0,0 +1,19 @@ +object Test { + def main(args: Array[String]): Unit = { + val t1 = B.EARTH + val t2 = B.JUPITER + + println("name: " + t1.name) + println("ordinal: " + t1.ordinal) + println("toString: " + t1.toString) + + val values: Array[A] = A.values + println("Values class: " + values.getClass) + values.foreach(v => println(v.name + " : " + v.ordinal)) + println("By-name value: " + A.valueOf("MONDAY")) + try A.valueOf("stuff") + catch { case e: IllegalArgumentException => + println("Correctly failed to retrieve illegal name, message: " + e.getMessage) + } + } +} \ No newline at end of file From 702ced50b369d08e517f8d674e2d07c10768f541 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Tue, 4 Jun 2019 19:13:52 +0200 Subject: [PATCH 06/20] Name removed from enum --- .../src/dotty/tools/dotc/ast/Desugar.scala | 6 +-- .../dotty/tools/dotc/ast/DesugarEnums.scala | 39 ++++++++---------- .../src/dotty/tools/dotc/core/StdNames.scala | 3 +- library/src/scala/Enum.scala | 3 -- .../pos-with-compiler/tasty/definitions.scala | 16 ++++---- tests/pos/enum-List-control.scala | 2 - tests/run/enum-Color.scala | 4 +- tests/run/enums-java-compat.check | 1 - tests/run/enums-java-compat.scala | 41 +++++++++++++++++++ tests/run/enums-java-compat/Enums.scala | 22 ---------- tests/run/enums-java-compat/Test.scala | 19 --------- 11 files changed, 70 insertions(+), 86 deletions(-) create mode 100644 tests/run/enums-java-compat.scala delete mode 100644 tests/run/enums-java-compat/Enums.scala delete mode 100644 tests/run/enums-java-compat/Test.scala diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 15f2f9dc5d00..406daf864cff 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -545,11 +545,7 @@ object desugar { yield syntheticProperty(nme.selectorName(i), caseParams(i).tpt, Select(This(EmptyTypeIdent), caseParams(i).name)) } - def ordinalMeths = - if (isEnumCase) - ordinalMethLit(nextOrdinal(CaseKind.Class)._1) :: - nameMethLit(className.toString) :: Nil - else Nil + def ordinalMeths = if (isEnumCase) ordinalMethLit(nextOrdinal(CaseKind.Class)._1) :: Nil else Nil def copyMeths = { val hasRepeatedParam = constrVparamss.exists(_.exists { case ValDef(_, tpt, _) => isRepeated(tpt) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index bd7c131903bf..f662817a995f 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -87,8 +87,8 @@ object DesugarEnums { /** The following lists of definitions for an enum type E: * * private val $values = new EnumValues[E] - * def valueOf(name: String) = - * try $values.fromName(name) catch + * def valueOf($name: String) = + * try $values.fromName($name) catch * { * case ex$:NoSuchElementException => * throw new IllegalArgumentException("key not found: ".concat(name)) @@ -105,9 +105,9 @@ object DesugarEnums { val valuesOfExnMessage = Apply( Select(Literal(Constant("key not found: ")), "concat".toTermName) - , Ident(nme.name) :: Nil) + , Ident(nme.nameDollar) :: Nil) val valuesOfBody = Try( - expr = Apply(valuesDot("fromName"), Ident(nme.name) :: Nil) + expr = Apply(valuesDot("fromName"), Ident(nme.nameDollar) :: Nil) , cases = CaseDef( pat = Typed(Ident(nme.DEFAULT_EXCEPTION_NAME), TypeTree(defn.NoSuchElementExceptionType)) , guard = EmptyTree @@ -115,7 +115,7 @@ object DesugarEnums { ) :: Nil , finalizer = EmptyTree ) - val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.name, defn.StringType) :: Nil), + val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.nameDollar, defn.StringType) :: Nil), TypeTree(), valuesOfBody) valuesDef :: @@ -125,25 +125,24 @@ object DesugarEnums { /** A creation method for a value of enum type `E`, which is defined as follows: * - * private def $new(tag: Int, name_: String) = new E { - * override def ordinal = tag - * override def name = name_ - * override def toString = name + * private def $new($tag: Int, $name: String) = new E { + * override def ordinal = $tag + * override def toString = $name * $values.register(this) * } */ private def enumValueCreator(implicit ctx: Context) = { - val ordinalDef = ordinalMeth(Ident(nme.tag)) - val nameDef = nameMeth(Ident(nme.name_)) + val ordinalDef = ordinalMeth(Ident(nme.tagDollar)) + val toStringDef = toStringMeth(Ident(nme.nameDollar)) val creator = New(Template( constr = emptyConstructor, parents = enumClassRef :: Nil, derived = Nil, self = EmptyValDef, - body = List(ordinalDef, nameDef, toStringMethAsName) ++ registerCall + body = List(ordinalDef, toStringDef) ++ registerCall ).withAttachment(ExtendsSingletonMirror, ())) DefDef(nme.DOLLAR_NEW, Nil, - List(List(param(nme.tag, defn.IntType), param(nme.name_, defn.StringType))), + List(List(param(nme.tagDollar, defn.IntType), param(nme.nameDollar, defn.StringType))), TypeTree(), creator).withFlags(Private | Synthetic) } @@ -269,20 +268,14 @@ object DesugarEnums { def ordinalMeth(body: Tree)(implicit ctx: Context): DefDef = DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withFlags(Override) - def nameMeth(body: Tree)(implicit ctx: Context): DefDef = - DefDef(nme.name, Nil, Nil, TypeTree(defn.StringType), body).withFlags(Override) - def toStringMeth(body: Tree)(implicit ctx: Context): DefDef = DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), body).withFlags(Override) def ordinalMethLit(ord: Int)(implicit ctx: Context): DefDef = ordinalMeth(Literal(Constant(ord))) - def nameMethLit(name: String)(implicit ctx: Context): DefDef = - nameMeth(Literal(Constant(name))) - - def toStringMethAsName(implicit ctx: Context): DefDef = - toStringMeth(Ident(nme.name)) + def toStringMethLit(name: String)(implicit ctx: Context): DefDef = + toStringMeth(Literal(Constant(name))) /** Expand a module definition representing a parameterless enum case */ def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, span: Span)(implicit ctx: Context): Tree = { @@ -293,8 +286,8 @@ object DesugarEnums { else { val (tag, scaffolding) = nextOrdinal(CaseKind.Object) val ordinalDef = ordinalMethLit(tag) - val nameDef = nameMethLit(name.toString) - val impl1 = cpy.Template(impl)(body = List(ordinalDef, nameDef, toStringMethAsName) ++ registerCall) + val toStringDef = toStringMethLit(name.toString) + val impl1 = cpy.Template(impl)(body = List(ordinalDef, toStringDef) ++ registerCall) .withAttachment(ExtendsSingletonMirror, ()) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index ab269f9200e8..b9ef1ed05e8d 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -484,7 +484,7 @@ object StdNames { val mirror : N = "mirror" val moduleClass : N = "moduleClass" val name: N = "name" - val name_ : N = "name_" + val nameDollar: N = "$name" val ne: N = "ne" val newFreeTerm: N = "newFreeTerm" val newFreeType: N = "newFreeType" @@ -539,6 +539,7 @@ object StdNames { val strictEquality: N = "strictEquality" val synchronized_ : N = "synchronized" val tag: N = "tag" + val tagDollar: N = "$tag" val tail: N = "tail" val `then` : N = "then" val this_ : N = "this" diff --git a/library/src/scala/Enum.scala b/library/src/scala/Enum.scala index 311d491305b7..5f0cf21dc223 100644 --- a/library/src/scala/Enum.scala +++ b/library/src/scala/Enum.scala @@ -5,7 +5,4 @@ trait Enum { /** A number uniquely identifying a case of an enum */ def ordinal: Int - - /** Name of the enum's case */ - def name: String } diff --git a/tests/pos-with-compiler/tasty/definitions.scala b/tests/pos-with-compiler/tasty/definitions.scala index 85b6e4855624..07e822cd851b 100644 --- a/tests/pos-with-compiler/tasty/definitions.scala +++ b/tests/pos-with-compiler/tasty/definitions.scala @@ -65,13 +65,13 @@ object definitions { /** Trees denoting terms */ enum Term extends Statement { def tpe: Type = ??? - case Ident(nme: String, override val tpe: Type) - case Select(prefix: Term, nme: String, signature: Option[Signature]) + case Ident(name: String, override val tpe: Type) + case Select(prefix: Term, name: String, signature: Option[Signature]) case Literal(value: Constant) case This(id: Option[Id]) case New(tpt: TypeTree) case Throw(expr: Term) - case NamedArg(nme: String, arg: Term) + case NamedArg(name: String, arg: Term) case Apply(fn: Term, args: List[Term]) case TypeApply(fn: Term, args: List[TypeTree]) case Super(thiz: Term, mixin: Option[Id]) @@ -94,9 +94,9 @@ object definitions { enum TypeTree extends Positioned { def tpe: Type = ??? case Synthetic() - case Ident(nme: String, override val tpe: Type) - case TermSelect(prefix: Term, nme: String) - case TypeSelect(prefix: TypeTree, nme: String) + case Ident(name: String, override val tpe: Type) + case TermSelect(prefix: Term, name: String) + case TypeSelect(prefix: TypeTree, name: String) case Singleton(ref: Term) case Refined(underlying: TypeTree, refinements: List[Definition]) case Applied(tycon: TypeTree, args: List[TypeTree | TypeBoundsTree]) @@ -105,7 +105,7 @@ object definitions { case Or(left: TypeTree, right: TypeTree) case ByName(tpt: TypeTree) case TypeLambda(tparams: List[TypeDef], body: Type | TypeBoundsTree) - case Bind(nme: String, bounds: TypeBoundsTree) + case Bind(name: String, bounds: TypeBoundsTree) } /** Trees denoting type bounds */ @@ -122,7 +122,7 @@ object definitions { enum Pattern extends Positioned { def tpe: Type = ??? case Value(v: Term) - case Bind(nme: String, pat: Pattern) + case Bind(name: String, pat: Pattern) case Unapply(unapply: Term, implicits: List[Term], pats: List[Pattern]) case Alternative(pats: List[Pattern]) case TypeTest(tpt: TypeTree) diff --git a/tests/pos/enum-List-control.scala b/tests/pos/enum-List-control.scala index f662e254b543..c8340b60bb5a 100644 --- a/tests/pos/enum-List-control.scala +++ b/tests/pos/enum-List-control.scala @@ -2,14 +2,12 @@ abstract sealed class List[T] extends Enum object List { final class Cons[T](x: T, xs: List[T]) extends List[T] { def ordinal = 0 - def name = "Cons" } object Cons { def apply[T](x: T, xs: List[T]): List[T] = new Cons(x, xs) } final class Nil[T]() extends List[T] { def ordinal = 1 - def name = "Nil" } object Nil { def apply[T](): List[T] = new Nil() diff --git a/tests/run/enum-Color.scala b/tests/run/enum-Color.scala index f48663b0005b..b00493dc00b9 100644 --- a/tests/run/enum-Color.scala +++ b/tests/run/enum-Color.scala @@ -6,8 +6,8 @@ enum Color { object Test { def main(args: Array[String]) = for (color <- Color.values) { - println(s"$color: ${color.name}") - assert(Color.valueOf(color.name) eq color) + println(s"$color: ${color.toString}") + assert(Color.valueOf(color.toString) eq color) import Color._ color match { case Red | Green | Blue => diff --git a/tests/run/enums-java-compat.check b/tests/run/enums-java-compat.check index 2005baaec107..04468ba9b44d 100644 --- a/tests/run/enums-java-compat.check +++ b/tests/run/enums-java-compat.check @@ -1,4 +1,3 @@ -name: EARTH ordinal: 0 toString: EARTH Values class: class [LA; diff --git a/tests/run/enums-java-compat.scala b/tests/run/enums-java-compat.scala new file mode 100644 index 000000000000..79369c5faa10 --- /dev/null +++ b/tests/run/enums-java-compat.scala @@ -0,0 +1,41 @@ +class JEnum { + def name: String = "Foo" + def ordinal: Int = 10 + def action = "fofofo" +} + +enum A extends JEnum { + case MONDAY, TUESDAY, SATURDAY + case Stuff + case Someday(x: String) + def report = "Reported" +} + +trait Foo1 +trait Bar + +enum B(val gravity: Double)(val isItGood: Boolean) extends Foo1 { + case EARTH extends B(9.8)(true) + case JUPITER extends B(100)(true) + case MOON extends B(4.3)(true) + case Foo extends B(10)(true) with Bar +} + +object Test { + def main(args: Array[String]): Unit = { + val t1 = B.EARTH + val t2 = B.JUPITER + + println("ordinal: " + t1.ordinal) + println("toString: " + t1.toString) + + val values: Array[A] = A.values + println("Values class: " + values.getClass) + values.foreach(v => println(v.toString + " : " + v.ordinal)) + println("By-name value: " + A.valueOf("MONDAY")) + try A.valueOf("stuff") + catch { case e: IllegalArgumentException => + println("Correctly failed to retrieve illegal name, message: " + e.getMessage) + } + } +} \ No newline at end of file diff --git a/tests/run/enums-java-compat/Enums.scala b/tests/run/enums-java-compat/Enums.scala deleted file mode 100644 index 1b7d541c99d8..000000000000 --- a/tests/run/enums-java-compat/Enums.scala +++ /dev/null @@ -1,22 +0,0 @@ -class JEnum { - def name: String = "Foo" - def ordinal: Int = 10 - def action = "fofofo" -} - -enum A extends JEnum { - case MONDAY, TUESDAY, SATURDAY - case Stuff - case Someday(x: String) - def report = "Reported" -} - -trait Foo1 -trait Bar - -enum B(val gravity: Double)(val isItGood: Boolean) extends Foo1 { - case EARTH extends B(9.8)(true) - case JUPITER extends B(100)(true) - case MOON extends B(4.3)(true) - case Foo extends B(10)(true) with Bar -} diff --git a/tests/run/enums-java-compat/Test.scala b/tests/run/enums-java-compat/Test.scala deleted file mode 100644 index d29429b70f2e..000000000000 --- a/tests/run/enums-java-compat/Test.scala +++ /dev/null @@ -1,19 +0,0 @@ -object Test { - def main(args: Array[String]): Unit = { - val t1 = B.EARTH - val t2 = B.JUPITER - - println("name: " + t1.name) - println("ordinal: " + t1.ordinal) - println("toString: " + t1.toString) - - val values: Array[A] = A.values - println("Values class: " + values.getClass) - values.foreach(v => println(v.name + " : " + v.ordinal)) - println("By-name value: " + A.valueOf("MONDAY")) - try A.valueOf("stuff") - catch { case e: IllegalArgumentException => - println("Correctly failed to retrieve illegal name, message: " + e.getMessage) - } - } -} \ No newline at end of file From 2d7570c49d42604ac5b9efcb61a2a9e380c45485 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 14:22:28 +0200 Subject: [PATCH 07/20] SyntheticMethods adapted to use ordinal instead of enumTag --- .../src/dotty/tools/dotc/transform/SyntheticMembers.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 60d98a74aaa8..2139553d7773 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -362,7 +362,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { /** For an enum T: * - * def ordinal(x: MirroredMonoType) = x.enumTag + * def ordinal(x: MirroredMonoType) = x.ordinal * * For sealed trait with children of normalized types C_1, ..., C_n: * @@ -377,7 +377,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * O is O.type. */ def ordinalBody(cls: Symbol, param: Tree)(implicit ctx: Context): Tree = - if (cls.is(Enum)) param.select(nme.enumTag) + if (cls.is(Enum)) param.select(nme.ordinal) else { val cases = for ((child, idx) <- cls.children.zipWithIndex) yield { From 35a20c946557938a27309fd8d6cc289a6d2e411b Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 14:47:19 +0200 Subject: [PATCH 08/20] Adapt CompleteJavaEnums to the new method names --- .../dotty/tools/dotc/transform/CompleteJavaEnums.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index b42fb3b14b24..e071a82348a8 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -87,15 +87,15 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => * 2. If this is a $new method that creates simple cases, pass $name and $ordinal parameters * to the enum superclass. The $new method looks like this: * - * def $new(..., enumTag: Int, name: String) = { + * def $new(..., ordinal: Int, name: String) = { * class $anon extends E(...) { ... } * new $anon * } * * After the transform it is expanded to * - * def $new(..., enumTag: Int, name: String) = { - * class $anon extends E(..., name, enumTag) { ... } + * def $new(..., ordinal: Int, name: String) = { + * class $anon extends E(..., name, ordinal) { ... } * new $anon * } */ @@ -124,7 +124,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => * * class $anon extends E(...) { * ... - * def enumTag = N + * def ordinal = N * def toString = S * ... * } @@ -150,7 +150,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => templ.body.collect { case mdef: DefDef if mdef.name == name => mdef.rhs }.head - val args = List(rhsOf(nme.toString_), rhsOf(nme.enumTag)) + val args = List(rhsOf(nme.toString_), rhsOf(nme.ordinal)) cpy.Template(templ)( parents = addEnumConstrArgs(cls.owner.owner.linkedClass, templ.parents, args)) } From c7a01495cd71e5f13197598f4cdfd720cee13fbd Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 15:19:12 +0200 Subject: [PATCH 09/20] SyntheticMethods' ordinal method calls the arg's ordinal method --- compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index 2139553d7773..aeaa5e056391 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -377,7 +377,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * O is O.type. */ def ordinalBody(cls: Symbol, param: Tree)(implicit ctx: Context): Tree = - if (cls.is(Enum)) param.select(nme.ordinal) + if (cls.is(Enum)) Apply(param.select(nme.ordinal), Nil) else { val cases = for ((child, idx) <- cls.children.zipWithIndex) yield { From 78a259604d0714aeae8600eb6d5b600998f71dfd Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 15:27:24 +0200 Subject: [PATCH 10/20] method added to enums to carry the ordinal number unconditionally --- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 10 +++++----- compiler/src/dotty/tools/dotc/core/StdNames.scala | 3 ++- library/src/scala/Enum.scala | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index f662817a995f..2f21bdaf14e5 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -132,7 +132,7 @@ object DesugarEnums { * } */ private def enumValueCreator(implicit ctx: Context) = { - val ordinalDef = ordinalMeth(Ident(nme.tagDollar)) + val ordinalDef = ordinalMeth(Ident(nme.ordinalDollar_)) val toStringDef = toStringMeth(Ident(nme.nameDollar)) val creator = New(Template( constr = emptyConstructor, @@ -142,9 +142,9 @@ object DesugarEnums { body = List(ordinalDef, toStringDef) ++ registerCall ).withAttachment(ExtendsSingletonMirror, ())) DefDef(nme.DOLLAR_NEW, Nil, - List(List(param(nme.tagDollar, defn.IntType), param(nme.nameDollar, defn.StringType))), + List(List(param(nme.nameDollar, defn.StringType), param(nme.ordinalDollar_, defn.IntType))), TypeTree(), creator).withFlags(Private | Synthetic) - } + }.reporting(e => s"marker\n${e.show}") /** The return type of an enum case apply method and any widening methods in which * the apply's right hand side will be wrapped. For parents of the form @@ -266,7 +266,7 @@ object DesugarEnums { ValDef(name, TypeTree(typ), EmptyTree).withFlags(Param) def ordinalMeth(body: Tree)(implicit ctx: Context): DefDef = - DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withFlags(Override) + DefDef(nme.ordinalDollar, Nil, Nil, TypeTree(defn.IntType), body) def toStringMeth(body: Tree)(implicit ctx: Context): DefDef = DefDef(nme.toString_, Nil, Nil, TypeTree(defn.StringType), body).withFlags(Override) @@ -304,7 +304,7 @@ object DesugarEnums { } else { val (tag, scaffolding) = nextOrdinal(CaseKind.Simple) - val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString)))) + val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(name.toString)), Literal(Constant(tag)))) val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index b9ef1ed05e8d..420e9190ad15 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -500,6 +500,8 @@ object StdNames { val ofDim: N = "ofDim" val opaque: N = "opaque" val ordinal: N = "ordinal" + val ordinalDollar: N = "$ordinal" + val ordinalDollar_ : N = "_$ordinal" val origin: N = "origin" val prefix : N = "prefix" val productArity: N = "productArity" @@ -539,7 +541,6 @@ object StdNames { val strictEquality: N = "strictEquality" val synchronized_ : N = "synchronized" val tag: N = "tag" - val tagDollar: N = "$tag" val tail: N = "tail" val `then` : N = "then" val this_ : N = "this" diff --git a/library/src/scala/Enum.scala b/library/src/scala/Enum.scala index 5f0cf21dc223..69f1daa27ab8 100644 --- a/library/src/scala/Enum.scala +++ b/library/src/scala/Enum.scala @@ -5,4 +5,5 @@ trait Enum { /** A number uniquely identifying a case of an enum */ def ordinal: Int + protected def $ordinal: Int } From c44bf64682fba246b914bc9e9924ac8c0dc12dc3 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 16:51:23 +0200 Subject: [PATCH 11/20] Synthetically generated oridnal method for enums --- .../src/dotty/tools/dotc/ast/DesugarEnums.scala | 6 +++--- .../src/dotty/tools/dotc/core/Definitions.scala | 4 ++++ .../dotc/transform/CompleteJavaEnums.scala | 6 +++--- .../tools/dotc/transform/SyntheticMembers.scala | 17 +++++++++++++++-- tests/run/enums-java-compat.scala | 1 - 5 files changed, 25 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 2f21bdaf14e5..eac670825657 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -142,9 +142,9 @@ object DesugarEnums { body = List(ordinalDef, toStringDef) ++ registerCall ).withAttachment(ExtendsSingletonMirror, ())) DefDef(nme.DOLLAR_NEW, Nil, - List(List(param(nme.nameDollar, defn.StringType), param(nme.ordinalDollar_, defn.IntType))), + List(List(param(nme.ordinalDollar_, defn.IntType), param(nme.nameDollar, defn.StringType))), TypeTree(), creator).withFlags(Private | Synthetic) - }.reporting(e => s"marker\n${e.show}") + } /** The return type of an enum case apply method and any widening methods in which * the apply's right hand side will be wrapped. For parents of the form @@ -304,7 +304,7 @@ object DesugarEnums { } else { val (tag, scaffolding) = nextOrdinal(CaseKind.Simple) - val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(name.toString)), Literal(Constant(tag)))) + val creator = Apply(Ident(nme.DOLLAR_NEW), List(Literal(Constant(tag)), Literal(Constant(name.toString)))) val vdef = ValDef(name, enumClassRef, creator).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index bb2fbc8148fd..b4667e51953e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -706,6 +706,10 @@ class Definitions { def NoneClass(implicit ctx: Context): ClassSymbol = NoneModuleRef.symbol.moduleClass.asClass @threadUnsafe lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum") def EnumClass(implicit ctx: Context): ClassSymbol = EnumType.symbol.asClass + @threadUnsafe lazy val Enum_ordinalR: TermRef = EnumClass.requiredMethodRef(nme.ordinal) + def Enum_ordinal(implicit ctx: Context): Symbol = Enum_ordinalR.symbol + @threadUnsafe lazy val JEnumType: TypeRef = ctx.requiredClassRef("scala.compat.JEnum") + def JEnumClass(implicit ctx: Context): ClassSymbol = JEnumType.symbol.asClass @threadUnsafe lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues") def EnumValuesClass(implicit ctx: Context): ClassSymbol = EnumValuesType.symbol.asClass @threadUnsafe lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product") diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index e071a82348a8..c7bd13ff15c8 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -17,8 +17,8 @@ import dotty.tools.dotc.ast.Trees._ object CompleteJavaEnums { val name: String = "completeJavaEnums" - private val nameParamName: TermName = "$name".toTermName - private val ordinalParamName: TermName = "$ordinal".toTermName + private val nameParamName: TermName = "_$name".toTermName + private val ordinalParamName: TermName = "_$ordinal".toTermName } /** For Scala enums that inherit from java.lang.Enum: @@ -150,7 +150,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => templ.body.collect { case mdef: DefDef if mdef.name == name => mdef.rhs }.head - val args = List(rhsOf(nme.toString_), rhsOf(nme.ordinal)) + val args = List(rhsOf(nme.toString_), rhsOf(nme.ordinalDollar)) cpy.Template(templ)( parents = addEnumConstrArgs(cls.owner.owner.linkedClass, templ.parents, args)) } diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index aeaa5e056391..b645a0bca425 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -56,6 +56,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { private[this] var myValueSymbols: List[Symbol] = Nil private[this] var myCaseSymbols: List[Symbol] = Nil private[this] var myCaseModuleSymbols: List[Symbol] = Nil + private[this] var myEnumCaseSymbols: List[Symbol] = Nil private def initSymbols(implicit ctx: Context) = if (myValueSymbols.isEmpty) { @@ -63,11 +64,13 @@ class SyntheticMembers(thisPhase: DenotTransformer) { myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity, defn.Product_productPrefix, defn.Product_productElement) myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals) + myEnumCaseSymbols = List(defn.Enum_ordinal) } def valueSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myValueSymbols } def caseSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseSymbols } def caseModuleSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseModuleSymbols } + def enumCaseSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myEnumCaseSymbols } private def existingDef(sym: Symbol, clazz: ClassSymbol)(implicit ctx: Context): Symbol = { val existing = sym.matchingMember(clazz.thisType) @@ -86,12 +89,15 @@ class SyntheticMembers(thisPhase: DenotTransformer) { lazy val accessors = if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased` else clazz.caseAccessors + val isEnumCase = clazz.derivesFrom(defn.EnumClass) val symbolsToSynthesize: List[Symbol] = if (clazz.is(Case)) { if (clazz.is(Module)) caseModuleSymbols + else if (isEnumCase) caseSymbols ++ enumCaseSymbols else caseSymbols } + else if (isEnumCase) enumCaseSymbols else if (isDerivedValueClass(clazz)) valueSymbols else Nil @@ -120,6 +126,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { case nme.productArity => Literal(Constant(accessors.length)) case nme.productPrefix => ownName case nme.productElement => productElementBody(accessors.length, vrefss.head.head) + case nme.ordinal => Select(This(clazz), nme.ordinalDollar) } ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") synthesizeDef(synthetic, treess => ctx => syntheticRHS(treess)(ctx)) @@ -362,7 +369,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { /** For an enum T: * - * def ordinal(x: MirroredMonoType) = x.ordinal + * def ordinal(x: MirroredMonoType) = x.$ordinal * * For sealed trait with children of normalized types C_1, ..., C_n: * @@ -377,7 +384,13 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * O is O.type. */ def ordinalBody(cls: Symbol, param: Tree)(implicit ctx: Context): Tree = - if (cls.is(Enum)) Apply(param.select(nme.ordinal), Nil) + if (cls.is(Enum)) { + val ordinalMeth = param.select(nme.ordinal) + val derivesFromJEnum = + cls.is(Enum, butNot = Case) && + cls.info.parents.exists(p => p.typeSymbol == defn.JEnumClass) + if (derivesFromJEnum) Apply(ordinalMeth, Nil) else ordinalMeth + } else { val cases = for ((child, idx) <- cls.children.zipWithIndex) yield { diff --git a/tests/run/enums-java-compat.scala b/tests/run/enums-java-compat.scala index 79369c5faa10..592e38c8d603 100644 --- a/tests/run/enums-java-compat.scala +++ b/tests/run/enums-java-compat.scala @@ -1,6 +1,5 @@ class JEnum { def name: String = "Foo" - def ordinal: Int = 10 def action = "fofofo" } From af652e978f89b9229940e4d22e2157693b2ecfb6 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 17:21:28 +0200 Subject: [PATCH 12/20] Fix documentation for --- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index eac670825657..2e0d076d16e8 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -126,7 +126,7 @@ object DesugarEnums { /** A creation method for a value of enum type `E`, which is defined as follows: * * private def $new($tag: Int, $name: String) = new E { - * override def ordinal = $tag + * def ordinal = $tag * override def toString = $name * $values.register(this) * } From 86939eb254c0793a5c3d60b3e539887e130a4beb Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 17:32:28 +0200 Subject: [PATCH 13/20] Do not generate synthetic ordinal for Enum itself --- .../src/dotty/tools/dotc/transform/SyntheticMembers.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index b645a0bca425..e2145436bdbf 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -64,7 +64,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { myCaseSymbols = myValueSymbols ++ List(defn.Any_toString, defn.Product_canEqual, defn.Product_productArity, defn.Product_productPrefix, defn.Product_productElement) myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals) - myEnumCaseSymbols = List(defn.Enum_ordinal) + myEnumCaseSymbols = List(defn.Enum_ordinal) } def valueSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myValueSymbols } @@ -89,7 +89,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { lazy val accessors = if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased` else clazz.caseAccessors - val isEnumCase = clazz.derivesFrom(defn.EnumClass) + val isEnumCase = clazz.derivesFrom(defn.EnumClass) && clazz != defn.EnumClass val symbolsToSynthesize: List[Symbol] = if (clazz.is(Case)) { From 1a42c3d1cd54cce524016c112ca999b70ce6db7d Mon Sep 17 00:00:00 2001 From: Anatolii Date: Wed, 5 Jun 2019 18:02:38 +0200 Subject: [PATCH 14/20] Fix enum-List-control test --- tests/pos/enum-List-control.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pos/enum-List-control.scala b/tests/pos/enum-List-control.scala index c8340b60bb5a..931406214122 100644 --- a/tests/pos/enum-List-control.scala +++ b/tests/pos/enum-List-control.scala @@ -1,13 +1,13 @@ abstract sealed class List[T] extends Enum object List { final class Cons[T](x: T, xs: List[T]) extends List[T] { - def ordinal = 0 + def $ordinal = 0 } object Cons { def apply[T](x: T, xs: List[T]): List[T] = new Cons(x, xs) } final class Nil[T]() extends List[T] { - def ordinal = 1 + def $ordinal = 1 } object Nil { def apply[T](): List[T] = new Nil() From 33f6c7783b3d4805a15cfd5e19109ad7d178d51a Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 6 Jun 2019 14:09:50 +0200 Subject: [PATCH 15/20] Stylistical improvements to the Enums code --- compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala | 9 +++------ .../dotty/tools/dotc/transform/SyntheticMembers.scala | 10 ++-------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 2e0d076d16e8..f06712f6cc3b 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -74,11 +74,8 @@ object DesugarEnums { else if (isEnumCase(cdef)) cdef.withMods(cdef.mods.withFlags(cdef.mods.flags | Final)) else cdef - private def valuesDotTerm(name: TermName)(implicit src: SourceFile) = - Select(Ident(nme.DOLLAR_VALUES), name) - - private def valuesDot(name: String)(implicit src: SourceFile) = - valuesDotTerm(name.toTermName) + private def valuesDot(name: PreName)(implicit src: SourceFile) = + Select(Ident(nme.DOLLAR_VALUES), name.toTermName) private def registerCall(implicit ctx: Context): List[Tree] = if (enumClass.typeParams.nonEmpty) Nil @@ -97,7 +94,7 @@ object DesugarEnums { */ private def enumScaffolding(implicit ctx: Context): List[Tree] = { val valuesDef = - DefDef(nme.values, Nil, Nil, TypeTree(), Select(valuesDotTerm(nme.values), nme.toArray)) + DefDef(nme.values, Nil, Nil, TypeTree(), Select(valuesDot(nme.values), nme.toArray)) val privateValuesDef = ValDef(nme.DOLLAR_VALUES, TypeTree(), New(TypeTree(defn.EnumValuesType.appliedTo(enumClass.typeRef :: Nil)), ListOfNil)) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index e2145436bdbf..318f16277066 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -369,7 +369,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { /** For an enum T: * - * def ordinal(x: MirroredMonoType) = x.$ordinal + * def ordinal(x: MirroredMonoType) = x.ordinal * * For sealed trait with children of normalized types C_1, ..., C_n: * @@ -384,13 +384,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { * O is O.type. */ def ordinalBody(cls: Symbol, param: Tree)(implicit ctx: Context): Tree = - if (cls.is(Enum)) { - val ordinalMeth = param.select(nme.ordinal) - val derivesFromJEnum = - cls.is(Enum, butNot = Case) && - cls.info.parents.exists(p => p.typeSymbol == defn.JEnumClass) - if (derivesFromJEnum) Apply(ordinalMeth, Nil) else ordinalMeth - } + if (cls.is(Enum)) param.select(nme.ordinal).ensureApplied else { val cases = for ((child, idx) <- cls.children.zipWithIndex) yield { From 5aa8d91bbf5ca8e7fb87010b3979fd993146ee44 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 6 Jun 2019 14:43:56 +0200 Subject: [PATCH 16/20] Documentation fixes for enums --- .../dotty/tools/dotc/ast/DesugarEnums.scala | 4 +- docs/docs/reference/enums/desugarEnums.md | 27 ++++++----- docs/docs/reference/enums/enums.md | 46 ++++++++++++------- 3 files changed, 45 insertions(+), 32 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index f06712f6cc3b..2ee262c38a7a 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -122,8 +122,8 @@ object DesugarEnums { /** 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 ordinal = $tag + * private def $new(_$ordinal: Int, $name: String) = new E { + * def $ordinal = $tag * override def toString = $name * $values.register(this) * } diff --git a/docs/docs/reference/enums/desugarEnums.md b/docs/docs/reference/enums/desugarEnums.md index 38b18fb3cb51..7bf8bf0615ae 100644 --- a/docs/docs/reference/enums/desugarEnums.md +++ b/docs/docs/reference/enums/desugarEnums.md @@ -120,11 +120,11 @@ map into case classes or vals. expands to a value definition in `E`'s companion object: - val C = new { ; def enumTag = n; $values.register(this) } + val C = new { ; def ordinal = n; $values.register(this) } where `n` is the ordinal number of the case in the companion object, starting from 0. The statement `$values.register(this)` registers the value - as one of the `enumValues` of the enumeration (see below). `$values` is a + as one of the `values` of the enumeration (see below). `$values` is a compiler-defined private value in the companion object. It is an error if a value case refers to a type parameter of the enclosing `enum` @@ -140,10 +140,10 @@ map into case classes or vals. However, unlike for a regular case class, the return type of the associated `apply` method is a fully parameterized type instance of the enum class `E` - itself instead of `C`. Also the enum case defines an `enumTag` method of + itself instead of `C`. Also the enum case defines an `ordinal` method of the form - def enumTag = n + def ordinal = n where `n` is the ordinal number of the case in the companion object, starting from 0. @@ -159,12 +159,9 @@ Non-generic enums `E` that define one or more singleton cases are called _enumerations_. Companion objects of enumerations define the following additional members. - - A method `enumValue` of type `scala.collection.immutable.Map[Int, E]`. - `enumValue(n)` returns the singleton case value with ordinal number `n`. - - A method `enumValueNamed` of type `scala.collection.immutable.Map[String, E]`. - `enumValueNamed(s)` returns the singleton case value whose `toString` - representation is `s`. - - A method `enumValues` which returns an `Iterable[E]` of all singleton case + - A method `valueOf(name: String): E`. It returns the singleton case value whose + `toString` representation is `name`. + - A method `values` which returns an `Array[E]` of all singleton case values in `E`, in the order of their definitions. Companion objects of enumerations that contain at least one simple case define in addition: @@ -173,12 +170,14 @@ Companion objects of enumerations that contain at least one simple case define i ordinal number and name. This method can be thought as being defined as follows. - def $new(tag: Int, name: String): ET = new E { - def enumTag = tag - def toString = name - $values.register(this) // register enum value so that `valueOf` and `values` can return it. + private def $new(\_$ordinal: Int, $name: String) = new E { + def $ordinal = $tag + override def toString = $name + $values.register(this) // register enum value so that `valueOf` and `values` can return it. } +The `$ordinal` method above is used to generate the `ordinal` method if the enum does not extend a `java.lang.Enum` (as Scala enums do not extend `java.lang.Enum`s unless explicitly specified). In case it does, there is no need to generate `ordinal` as `java.lang.Enum` defines it. + ### Scopes for Enum Cases A case in an `enum` is treated similarly to a secondary constructor. It can access neither the enclosing `enum` using `this`, nor its value parameters or instance members using simple diff --git a/docs/docs/reference/enums/enums.md b/docs/docs/reference/enums/enums.md index 50599b66fba0..5af13b211262 100644 --- a/docs/docs/reference/enums/enums.md +++ b/docs/docs/reference/enums/enums.md @@ -33,27 +33,25 @@ explicit extends clause. ### Methods defined for enums The values of an enum correspond to unique integers. The integer -associated with an enum value is returned by its `enumTag` method: +associated with an enum value is returned by its `ordinal` method: ```scala scala> val red = Color.Red val red: Color = Red -scala> red.enumTag +scala> red.ordinal val res0: Int = 0 ``` -The companion object of an enum also defines three utility methods. -The `enumValue` and `enumValueNamed` methods obtain an enum value -by its tag or its name. The `enumValues` method returns all enum values -defined in an enumeration in an `Iterable`. +The companion object of an enum also defines two utility methods. +The `valueOf` method obtains an enum value +by its name. The `values` method returns all enum values +defined in an enumeration in an `Array`. ```scala -scala> Color.enumValue(1) -val res1: Color = Green -scala> Color.enumValueNamed("Blue") -val res2: Color = Blue -scala> Color.enumValues -val res3: collection.Iterable[Color] = MapLike(Red, Green, Blue) +scala> Color.valueOf("Blue") +val res0: Color = Blue +scala> Color.values +val res1: Array[Color] = Array(Red, Green, Blue) ``` ### User-defined members of enums @@ -84,16 +82,32 @@ object Planet { def main(args: Array[String]) = { val earthWeight = args(0).toDouble val mass = earthWeight / Earth.surfaceGravity - for (p <- enumValues) + for (p <- values) println(s"Your weight on $p is ${p.surfaceWeight(mass)}") } } ``` +### Compatibility with Java Enums +If you want to use the Scala-defined enums as Java enums, you can do so by extending `compat.JEnum` class as follows: + +```scala +enum Color extends compat.JEnum[Color] { case Red, Green, Blue } +``` + +The type parameter comes from the Java enum [definition](https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/Enum.html) and should me the same as the type of the enum. The compiler will transform the definition above so that `Color` extends `java.lang.Enum`. + +After defining `Color` like that, you can use like you would a Java enum: + +```scala +scala> Color.Red.compareTo(Color.Green) +val res15: Int = -1 +``` + ### Implementation Enums are represented as `sealed` classes that extend the `scala.Enum` trait. -This trait defines a single method, `enumTag`: +This trait defines a single public method, `ordinal`: ```scala package scala @@ -102,7 +116,7 @@ package scala trait Enum { /** A number uniquely identifying a case of an enum */ - def enumTag: Int + def ordinal: Int } ``` @@ -112,7 +126,7 @@ For instance, the `Venus` value above would be defined like this: ```scala val Venus: Planet = new Planet(4.869E24, 6051800.0) { - def enumTag: Int = 1 + def ordinal: Int = 1 override def toString: String = "Venus" // internal code to register value } From 857549fe5a1bf177a9578ceddba21eb34eb1a95a Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 6 Jun 2019 17:35:45 +0200 Subject: [PATCH 17/20] Generate ACC_ENUM flags for Java enums --- compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala | 3 ++- compiler/src/dotty/tools/backend/jvm/BackendInterface.scala | 1 + .../src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 897537df047d..377e7cd96bf4 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -223,7 +223,8 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { if (sym.hasEnumFlag) ACC_ENUM else 0, if (sym.isVarargsMethod) ACC_VARARGS else 0, if (sym.isSynchronized) ACC_SYNCHRONIZED else 0, - if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0 + if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0, + if (sym.isEnum) asm.Opcodes.ACC_ENUM else 0 ) } diff --git a/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala index 66fda62641c6..d428dd7e1562 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala @@ -512,6 +512,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions { def isJavaDefaultMethod: Boolean def isClassConstructor: Boolean def isSerializable: Boolean + def isEnum: Boolean /** * True for module classes of modules that are top-level or owned only by objects. Module classes diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 3cd668e1fc38..90240ca0553a 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -703,6 +703,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def shouldEmitForwarders: Boolean = (sym is Flags.Module) && sym.isStatic def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntryPoint(sym) + def isEnum = sym.is(Flags.Enum, butNot = Flags.Case) && sym.info.parents.exists(p => p.typeSymbol == defn.JEnumClass) def isClassConstructor: Boolean = toDenot(sym).isClassConstructor def isSerializable: Boolean = toDenot(sym).isSerializable From 9abfef840f5e74fe2d3f9bff667d2916e4df6fbd Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 6 Jun 2019 20:07:10 +0200 Subject: [PATCH 18/20] Make enums compatible with the new constructor hijack --- .../tools/backend/jvm/BTypesFromSymbols.scala | 2 +- .../tools/backend/jvm/BackendInterface.scala | 2 +- .../backend/jvm/DottyBackendInterface.scala | 2 +- .../dotty/tools/dotc/ast/DesugarEnums.scala | 18 +++++++++--------- .../dotty/tools/dotc/core/Definitions.scala | 6 ++---- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala index 377e7cd96bf4..7ba33941eb5a 100644 --- a/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala +++ b/compiler/src/dotty/tools/backend/jvm/BTypesFromSymbols.scala @@ -224,7 +224,7 @@ class BTypesFromSymbols[I <: BackendInterface](val int: I) extends BTypes { if (sym.isVarargsMethod) ACC_VARARGS else 0, if (sym.isSynchronized) ACC_SYNCHRONIZED else 0, if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0, - if (sym.isEnum) asm.Opcodes.ACC_ENUM else 0 + if (sym.isJavaEnum) asm.Opcodes.ACC_ENUM else 0 ) } diff --git a/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala index d428dd7e1562..0ffb9af33156 100644 --- a/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/BackendInterface.scala @@ -512,7 +512,7 @@ abstract class BackendInterface extends BackendInterfaceDefinitions { def isJavaDefaultMethod: Boolean def isClassConstructor: Boolean def isSerializable: Boolean - def isEnum: Boolean + def isJavaEnum: Boolean /** * True for module classes of modules that are top-level or owned only by objects. Module classes diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 90240ca0553a..5069329f685c 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -703,7 +703,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def shouldEmitForwarders: Boolean = (sym is Flags.Module) && sym.isStatic def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntryPoint(sym) - def isEnum = sym.is(Flags.Enum, butNot = Flags.Case) && sym.info.parents.exists(p => p.typeSymbol == defn.JEnumClass) + def isJavaEnum = sym.is(Flags.Enum, butNot = Flags.Case) && sym.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass) def isClassConstructor: Boolean = toDenot(sym).isClassConstructor def isSerializable: Boolean = toDenot(sym).isSerializable diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 2ee262c38a7a..afc295409142 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -101,16 +101,16 @@ object DesugarEnums { .withFlags(Private) val valuesOfExnMessage = Apply( - Select(Literal(Constant("key not found: ")), "concat".toTermName) - , Ident(nme.nameDollar) :: Nil) + Select(Literal(Constant("key not found: ")), "concat".toTermName), + Ident(nme.nameDollar) :: Nil) val valuesOfBody = Try( - expr = Apply(valuesDot("fromName"), Ident(nme.nameDollar) :: Nil) - , cases = CaseDef( - pat = Typed(Ident(nme.DEFAULT_EXCEPTION_NAME), TypeTree(defn.NoSuchElementExceptionType)) - , guard = EmptyTree - , body = Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(valuesOfExnMessage :: Nil))) - ) :: Nil - , finalizer = EmptyTree + expr = Apply(valuesDot("fromName"), Ident(nme.nameDollar) :: Nil), + cases = CaseDef( + pat = Typed(Ident(nme.DEFAULT_EXCEPTION_NAME), TypeTree(defn.NoSuchElementExceptionType)), + guard = EmptyTree, + body = Throw(New(TypeTree(defn.IllegalArgumentExceptionType), List(valuesOfExnMessage :: Nil))) + ) :: Nil, + finalizer = EmptyTree ) val valueOfDef = DefDef(nme.valueOf, Nil, List(param(nme.nameDollar, defn.StringType) :: Nil), TypeTree(), valuesOfBody) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b4667e51953e..2e046eb783c8 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -605,9 +605,9 @@ class Definitions { @threadUnsafe lazy val SystemClass: ClassSymbol = ctx.requiredClass("java.lang.System") @threadUnsafe lazy val SystemModule: Symbol = SystemClass.linkedClass - lazy val NoSuchElementExceptionClass = ctx.requiredClass("java.util.NoSuchElementException") + @threadUnsafe lazy val NoSuchElementExceptionClass = ctx.requiredClass("java.util.NoSuchElementException") def NoSuchElementExceptionType = NoSuchElementExceptionClass.typeRef - lazy val IllegalArgumentExceptionClass = ctx.requiredClass("java.lang.IllegalArgumentException") + @threadUnsafe lazy val IllegalArgumentExceptionClass = ctx.requiredClass("java.lang.IllegalArgumentException") def IllegalArgumentExceptionType = IllegalArgumentExceptionClass.typeRef // in scalac modified to have Any as parent @@ -708,8 +708,6 @@ class Definitions { def EnumClass(implicit ctx: Context): ClassSymbol = EnumType.symbol.asClass @threadUnsafe lazy val Enum_ordinalR: TermRef = EnumClass.requiredMethodRef(nme.ordinal) def Enum_ordinal(implicit ctx: Context): Symbol = Enum_ordinalR.symbol - @threadUnsafe lazy val JEnumType: TypeRef = ctx.requiredClassRef("scala.compat.JEnum") - def JEnumClass(implicit ctx: Context): ClassSymbol = JEnumType.symbol.asClass @threadUnsafe lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues") def EnumValuesClass(implicit ctx: Context): ClassSymbol = EnumValuesType.symbol.asClass @threadUnsafe lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product") From 79db9f7050d443df45ecc9b459f70d3f60d6c794 Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 6 Jun 2019 20:18:50 +0200 Subject: [PATCH 19/20] Add tests for enums interop with Java efficient collections --- tests/run/enum-efficient-collections.check | 6 ++++++ tests/run/enum-efficient-collections.scala | 16 ++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 tests/run/enum-efficient-collections.check create mode 100644 tests/run/enum-efficient-collections.scala diff --git a/tests/run/enum-efficient-collections.check b/tests/run/enum-efficient-collections.check new file mode 100644 index 000000000000..3dff8bb9a658 --- /dev/null +++ b/tests/run/enum-efficient-collections.check @@ -0,0 +1,6 @@ +MONDAY +TUESDAY +SATURDAY +WEDNESDAY +workday +weekend diff --git a/tests/run/enum-efficient-collections.scala b/tests/run/enum-efficient-collections.scala new file mode 100644 index 000000000000..01aeb97be014 --- /dev/null +++ b/tests/run/enum-efficient-collections.scala @@ -0,0 +1,16 @@ +enum Day extends java.lang.Enum[Day] { + case MONDAY, TUESDAY, SATURDAY, WEDNESDAY +} + +object Test { + def main(args: Array[String]): Unit = { + val allDays = java.util.EnumSet.allOf(classOf[Day]) + val dayMap = new java.util.EnumMap[Day, String](classOf[Day]) + dayMap.put(Day.MONDAY, "workday") + dayMap.put(Day.SATURDAY, "weekend") + + allDays.toArray.foreach(println) + println(dayMap.get(Day.MONDAY)) + println(dayMap.get(Day.SATURDAY)) + } +} From 0b4c54630e5c6bb1eb7e8a56e564ae6868d2886c Mon Sep 17 00:00:00 2001 From: Anatolii Date: Thu, 6 Jun 2019 20:19:22 +0200 Subject: [PATCH 20/20] Factor out derivesFromJavaEnum to SymUtils --- .../backend/jvm/DottyBackendInterface.scala | 2 +- .../dotc/transform/CompleteJavaEnums.scala | 17 ++++++----------- .../dotty/tools/dotc/transform/SymUtils.scala | 4 ++++ 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 5069329f685c..bd7ead685a77 100644 --- a/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -703,7 +703,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma def shouldEmitForwarders: Boolean = (sym is Flags.Module) && sym.isStatic def isJavaEntryPoint: Boolean = CollectEntryPoints.isJavaEntryPoint(sym) - def isJavaEnum = sym.is(Flags.Enum, butNot = Flags.Case) && sym.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass) + def isJavaEnum = sym.derivesFromJavaEnum def isClassConstructor: Boolean = toDenot(sym).isClassConstructor def isSerializable: Boolean = toDenot(sym).isSerializable diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index c7bd13ff15c8..5aa006ebdaca 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -13,6 +13,7 @@ import Constants._ import Decorators._ import DenotTransformers._ import dotty.tools.dotc.ast.Trees._ +import SymUtils._ object CompleteJavaEnums { val name: String = "completeJavaEnums" @@ -37,16 +38,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = if (sym.isConstructor && ( sym == defn.JavaEnumClass.primaryConstructor || - derivesFromJavaEnum(sym.owner))) + sym.owner.derivesFromJavaEnum)) addConstrParams(sym.info) else tp - /** Is `sym` a Scala enum class that derives (directly) from `java.lang.Enum`? - */ - private def derivesFromJavaEnum(sym: Symbol)(implicit ctx: Context) = - sym.is(Enum, butNot = Case) && - sym.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass) - /** Add constructor parameters `$name: String` and `$ordinal: Int` to the end of * the last parameter list of (method- or poly-) type `tp`. */ @@ -101,10 +96,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => */ override def transformDefDef(tree: DefDef)(implicit ctx: Context): DefDef = { val sym = tree.symbol - if (sym.isConstructor && derivesFromJavaEnum(sym.owner)) + if (sym.isConstructor && sym.owner.derivesFromJavaEnum) cpy.DefDef(tree)( vparamss = tree.vparamss.init :+ (tree.vparamss.last ++ addedParams(sym, Param))) - else if (sym.name == nme.DOLLAR_NEW && derivesFromJavaEnum(sym.owner.linkedClass)) { + else if (sym.name == nme.DOLLAR_NEW && sym.owner.linkedClass.derivesFromJavaEnum) { val Block((tdef @ TypeDef(tpnme.ANON_CLASS, templ: Template)) :: Nil, call) = tree.rhs val args = tree.vparamss.last.takeRight(2).map(param => ref(param.symbol)).reverse val templ1 = cpy.Template(templ)( @@ -137,7 +132,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => */ override def transformTemplate(templ: Template)(implicit ctx: Context): Template = { val cls = templ.symbol.owner - if (derivesFromJavaEnum(cls)) { + if (cls.derivesFromJavaEnum) { val (params, rest) = decomposeTemplateBody(templ.body) val addedDefs = addedParams(cls, ParamAccessor) val addedSyms = addedDefs.map(_.symbol.entered) @@ -145,7 +140,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => parents = addEnumConstrArgs(defn.JavaEnumClass, templ.parents, addedSyms.map(ref)), body = params ++ addedDefs ++ rest) } - else if (cls.isAnonymousClass && cls.owner.is(EnumCase) && derivesFromJavaEnum(cls.owner.owner.linkedClass)) { + else if (cls.isAnonymousClass && cls.owner.is(EnumCase) && cls.owner.owner.linkedClass.derivesFromJavaEnum) { def rhsOf(name: TermName) = templ.body.collect { case mdef: DefDef if mdef.name == name => mdef.rhs diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 5c25530b247d..2695f1486487 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -67,6 +67,10 @@ class SymUtils(val self: Symbol) extends AnyVal { def isParamOrAccessor(implicit ctx: Context): Boolean = self.is(Param) || self.is(ParamAccessor) + def derivesFromJavaEnum(implicit ctx: Context) = + self.is(Enum, butNot = Case) && + self.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass) + /** Is this a case class for which a product mirror is generated? * Excluded are value classes, abstract classes and case classes with more than one * parameter section.