From 48cec48f33caa6b67c5c21a05139d0add95f9c39 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 19:59:13 +0100 Subject: [PATCH 01/13] Fix #7991: don't set JavaDefined for Dotty Enum module class Top-level Dotty Enum classes have the flag JAVA_ACC_ENUM. We cannot tell from the flag whether a class is JavaDefined or not. The `moduleRoot` already has the flag `JavaDefined` set, it suffices to test the flag. --- .../dotty/tools/dotc/core/classfile/ClassfileParser.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index 966ccb90cbdd..cb264eef734f 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -200,12 +200,13 @@ class ClassfileParser( sym.markAbsent() } - // eager load java enum definitions for exhaustivity check of pattern match + // eager load enum definitions for exhaustivity check of pattern match if (isEnum) { instanceScope.toList.map(_.ensureCompleted()) staticScope.toList.map(_.ensureCompleted()) - classRoot.setFlag(Flags.JavaEnumTrait) - moduleRoot.setFlag(Flags.JavaEnumTrait) + val flag = if moduleRoot.is(Flags.JavaDefined) then Flags.JavaEnumTrait else Flags.Enum + classRoot.setFlag(flag) + moduleRoot.setFlag(flag) } result From 50310d22c669803f8226d9d0ce48f9e17afff7ca Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 20:06:38 +0100 Subject: [PATCH 02/13] Add test --- tests/run/i7991.scala | 5 +++++ tests/run/i7991/Num_1.scala | 1 + tests/run/i7991/Test_2.scala | 3 +++ 3 files changed, 9 insertions(+) create mode 100644 tests/run/i7991.scala create mode 100644 tests/run/i7991/Num_1.scala create mode 100644 tests/run/i7991/Test_2.scala diff --git a/tests/run/i7991.scala b/tests/run/i7991.scala new file mode 100644 index 000000000000..27ba1e64cc53 --- /dev/null +++ b/tests/run/i7991.scala @@ -0,0 +1,5 @@ +enum Num { case One } + +object Test extends App { + Num.One +} \ No newline at end of file diff --git a/tests/run/i7991/Num_1.scala b/tests/run/i7991/Num_1.scala new file mode 100644 index 000000000000..cc35f91c9dab --- /dev/null +++ b/tests/run/i7991/Num_1.scala @@ -0,0 +1 @@ +enum Num { case One } \ No newline at end of file diff --git a/tests/run/i7991/Test_2.scala b/tests/run/i7991/Test_2.scala new file mode 100644 index 000000000000..14e4fb704344 --- /dev/null +++ b/tests/run/i7991/Test_2.scala @@ -0,0 +1,3 @@ +object Test extends App { + Num.One +} From abd4a35b15e0818e20d708a4343cd45c6e5f7dac Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 20:17:21 +0100 Subject: [PATCH 03/13] Fix #7410: add test --- tests/run/i7410/Enum_1.scala | 3 +++ tests/run/i7410/Test_2.scala | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/run/i7410/Enum_1.scala create mode 100644 tests/run/i7410/Test_2.scala diff --git a/tests/run/i7410/Enum_1.scala b/tests/run/i7410/Enum_1.scala new file mode 100644 index 000000000000..228bb3699c4f --- /dev/null +++ b/tests/run/i7410/Enum_1.scala @@ -0,0 +1,3 @@ +enum E { + case A, B, C +} diff --git a/tests/run/i7410/Test_2.scala b/tests/run/i7410/Test_2.scala new file mode 100644 index 000000000000..a2e22261a4a6 --- /dev/null +++ b/tests/run/i7410/Test_2.scala @@ -0,0 +1,6 @@ +object Test { + def main(args: Array[String]): Unit = { + val a = E.A + println(a) + } +} \ No newline at end of file From 1e3a77bb93977c21c27eefa58969b251f458cbfe Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 20:50:29 +0100 Subject: [PATCH 04/13] Fix #6677: add test --- tests/run/i6677/Enum_1.scala | 3 +++ tests/run/i6677/Test_2.scala | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 tests/run/i6677/Enum_1.scala create mode 100644 tests/run/i6677/Test_2.scala diff --git a/tests/run/i6677/Enum_1.scala b/tests/run/i6677/Enum_1.scala new file mode 100644 index 000000000000..260f450686fb --- /dev/null +++ b/tests/run/i6677/Enum_1.scala @@ -0,0 +1,3 @@ +enum Foo[A] { + case Bar extends Foo[Int] +} diff --git a/tests/run/i6677/Test_2.scala b/tests/run/i6677/Test_2.scala new file mode 100644 index 000000000000..62f77cc4d27e --- /dev/null +++ b/tests/run/i6677/Test_2.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + println(Foo.Bar) + } +} \ No newline at end of file From ee60cc0e3d103b1ff7c6e66e940d980f236e56e1 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 20:56:38 +0100 Subject: [PATCH 05/13] Fix #7287: add test --- tests/run/i7287/Enum_1.scala | 5 +++++ tests/run/i7287/Test_2.scala | 6 ++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/run/i7287/Enum_1.scala create mode 100644 tests/run/i7287/Test_2.scala diff --git a/tests/run/i7287/Enum_1.scala b/tests/run/i7287/Enum_1.scala new file mode 100644 index 000000000000..94ce29fa206b --- /dev/null +++ b/tests/run/i7287/Enum_1.scala @@ -0,0 +1,5 @@ +enum Color derives Eql { + case Unknown + case Blue(v: Int) + case Red(v: Int) +} diff --git a/tests/run/i7287/Test_2.scala b/tests/run/i7287/Test_2.scala new file mode 100644 index 000000000000..8f3fabf18dc5 --- /dev/null +++ b/tests/run/i7287/Test_2.scala @@ -0,0 +1,6 @@ +object Test { + def main(args: Array[String]): Unit = { + val testData = Seq(("blah", Color.Unknown), ("red", Color.Red(10))) + println(testData) + } +} \ No newline at end of file From b7910d3d96ce33810c72a312832b8a29e28d3c6c Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 21:08:51 +0100 Subject: [PATCH 06/13] Fix #7424: add test --- tests/run/i7424/Enum_1.scala | 3 +++ tests/run/i7424/Test_2.scala | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 tests/run/i7424/Enum_1.scala create mode 100644 tests/run/i7424/Test_2.scala diff --git a/tests/run/i7424/Enum_1.scala b/tests/run/i7424/Enum_1.scala new file mode 100644 index 000000000000..7b19fc5b6eba --- /dev/null +++ b/tests/run/i7424/Enum_1.scala @@ -0,0 +1,3 @@ +enum A { + case A1 +} diff --git a/tests/run/i7424/Test_2.scala b/tests/run/i7424/Test_2.scala new file mode 100644 index 000000000000..cc3b5fbdac7f --- /dev/null +++ b/tests/run/i7424/Test_2.scala @@ -0,0 +1,5 @@ +object Test { + def main(args: Array[String]): Unit = { + println(A.values) + } +} \ No newline at end of file From 5e133054688beacfbbac90d19abbe8a57701ec9f Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 21:09:23 +0100 Subject: [PATCH 07/13] Fix #6664: add test --- tests/run/i6664/Bomb_1.scala | 4 ++++ tests/run/i6664/Detonator_2.scala | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 tests/run/i6664/Bomb_1.scala create mode 100644 tests/run/i6664/Detonator_2.scala diff --git a/tests/run/i6664/Bomb_1.scala b/tests/run/i6664/Bomb_1.scala new file mode 100644 index 000000000000..fb1603635ef0 --- /dev/null +++ b/tests/run/i6664/Bomb_1.scala @@ -0,0 +1,4 @@ +enum Bomb +{ + case Kaboom +} \ No newline at end of file diff --git a/tests/run/i6664/Detonator_2.scala b/tests/run/i6664/Detonator_2.scala new file mode 100644 index 000000000000..690780d47d4f --- /dev/null +++ b/tests/run/i6664/Detonator_2.scala @@ -0,0 +1,5 @@ +object Test +{ + def boom: Unit = Bomb.valueOf("Kaboom") + def main(args: Array[String]): Unit = println("ok!") +} \ No newline at end of file From c9184d42dda19e46d34442ce957af1b66fe8d825 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Wed, 15 Jan 2020 21:38:54 +0100 Subject: [PATCH 08/13] Unify handling of flags (thanks @smarter) --- .../dotty/tools/dotc/core/classfile/ClassfileConstants.scala | 1 + .../src/dotty/tools/dotc/core/classfile/ClassfileParser.scala | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala index 02cebe2541ab..b57404a77377 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileConstants.scala @@ -343,6 +343,7 @@ object ClassfileConstants { case JAVA_ACC_FINAL => Final case JAVA_ACC_SYNTHETIC => Synthetic case JAVA_ACC_STATIC => JavaStatic + case JAVA_ACC_ENUM => Enum case JAVA_ACC_ABSTRACT => if (isClass) Abstract else Deferred case JAVA_ACC_INTERFACE => PureInterfaceCreationFlags | JavaDefined case _ => EmptyFlags diff --git a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala index cb264eef734f..6b85cec70d7a 100644 --- a/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala +++ b/compiler/src/dotty/tools/dotc/core/classfile/ClassfileParser.scala @@ -204,9 +204,6 @@ class ClassfileParser( if (isEnum) { instanceScope.toList.map(_.ensureCompleted()) staticScope.toList.map(_.ensureCompleted()) - val flag = if moduleRoot.is(Flags.JavaDefined) then Flags.JavaEnumTrait else Flags.Enum - classRoot.setFlag(flag) - moduleRoot.setFlag(flag) } result From 832847f45905dd8282676b2d5f849ce85fd07d0e Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 16 Jan 2020 20:20:12 +0100 Subject: [PATCH 09/13] Add failing test --- tests/run/i6664b/Bomb_1.scala | 4 ++++ tests/run/i6664b/Detonator_2.scala | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 tests/run/i6664b/Bomb_1.scala create mode 100644 tests/run/i6664b/Detonator_2.scala diff --git a/tests/run/i6664b/Bomb_1.scala b/tests/run/i6664b/Bomb_1.scala new file mode 100644 index 000000000000..e50d06ab1d0e --- /dev/null +++ b/tests/run/i6664b/Bomb_1.scala @@ -0,0 +1,4 @@ +enum Bomb extends java.lang.Enum[Bomb] +{ + case Kaboom +} \ No newline at end of file diff --git a/tests/run/i6664b/Detonator_2.scala b/tests/run/i6664b/Detonator_2.scala new file mode 100644 index 000000000000..4c4c74a24eed --- /dev/null +++ b/tests/run/i6664b/Detonator_2.scala @@ -0,0 +1,4 @@ +object Test +{ + def main(args: Array[String]): Unit = println(Bomb.Kaboom) +} \ No newline at end of file From 918338c3e613df81edbced76c37e8305a3ae7851 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 16 Jan 2020 22:03:03 +0100 Subject: [PATCH 10/13] Use forwarders for java interop instead of moving definitions Moving definitions will make Scala code crash at runtime. See `tests/run/i6664b` for an example. --- .../dotc/transform/CompleteJavaEnums.scala | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index 75f5d453fe5a..fbab1c9bfb51 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -110,11 +110,19 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => else tree } - override def transformValDef(tree: ValDef)(implicit ctx: Context): ValDef = { - val sym = tree.symbol - if ((sym.isAllOf(EnumValue) || sym.name == nme.DOLLAR_VALUES) && sym.owner.linkedClass.derivesFromJavaEnum) - sym.addAnnotation(Annotations.Annotation(defn.ScalaStaticAnnot)) - tree + /** Return a list of forwarders for enum values defined in the companion object + * for java interop. + */ + private def addedEnumForwarders(clazz: Symbol)(implicit ctx: Context): List[ValDef] = { + val moduleCls = clazz.companionClass + val moduleRef = ref(clazz.companionModule) + + val enums = moduleCls.info.decls.filter(member => member.isTerm && member.isAllOf(EnumValue)) + for { enumValue <- enums } + yield { + val fieldSym = ctx.newSymbol(clazz, enumValue.name.asTermName, EnumValue, enumValue.info) + ValDef(fieldSym, moduleRef.select(enumValue)) + } } /** 1. If this is an enum class, add $name and $ordinal parameters to its @@ -143,9 +151,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => val (params, rest) = decomposeTemplateBody(templ.body) val addedDefs = addedParams(cls, ParamAccessor) val addedSyms = addedDefs.map(_.symbol.entered) + val addedForwarders = addedEnumForwarders(cls) cpy.Template(templ)( parents = addEnumConstrArgs(defn.JavaEnumClass, templ.parents, addedSyms.map(ref)), - body = params ++ addedDefs ++ rest) + body = params ++ addedDefs ++ addedForwarders ++ rest) } else if (cls.isAnonymousClass && cls.owner.isAllOf(EnumCase) && cls.owner.owner.linkedClass.derivesFromJavaEnum) { def rhsOf(name: TermName) = From 96ae5aed35457c1984105e246cbdadfaaaef37f2 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 16 Jan 2020 22:15:40 +0100 Subject: [PATCH 11/13] Add repl test --- compiler/test-resources/repl/i7410 | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 compiler/test-resources/repl/i7410 diff --git a/compiler/test-resources/repl/i7410 b/compiler/test-resources/repl/i7410 new file mode 100644 index 000000000000..b2846e94baf6 --- /dev/null +++ b/compiler/test-resources/repl/i7410 @@ -0,0 +1,4 @@ +> enum E { case A, B, C } +// defined class E +> E.A +val res0: E = A \ No newline at end of file From f6f3b28cfcc1f79d002a8af950ef49a84bc88b07 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 16 Jan 2020 23:42:41 +0100 Subject: [PATCH 12/13] Initialize static fields correctly Currently the only way to put initializer inside the static constructor is to use @static. However, CheckStatic restricts that @static can only used in companion object, which is a user-facing check, as MoveStatics can handle @static member in the class. We need a refactoring to make @static a general mechanism that includes a phase to generate the static constructor. This needs to be addressed in another PR. --- compiler/src/dotty/tools/dotc/Compiler.scala | 4 ++-- .../src/dotty/tools/dotc/transform/CompleteJavaEnums.scala | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 32cee5faaa3f..8f4ab656881b 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -58,8 +58,8 @@ class Compiler { new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars new ElimPackagePrefixes, // Eliminate references to package prefixes in Select nodes new CookComments, // Cook the comments: expand variables, doc, etc. - new CompleteJavaEnums) :: // Fill in constructors for Java enums - List(new CheckStatic, // Check restrictions that apply to @static members + new CheckStatic) :: // Check restrictions that apply to @static members + List(new CompleteJavaEnums, // Fill in constructors for Java enums new ElimRepeated, // Rewrite vararg parameters and arguments new ExpandSAMs, // Expand single abstract method closures to anonymous classes new ProtectedAccessors, // Add accessors for protected members diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index fbab1c9bfb51..eed261f62115 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -117,10 +117,11 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => val moduleCls = clazz.companionClass val moduleRef = ref(clazz.companionModule) - val enums = moduleCls.info.decls.filter(member => member.isTerm && member.isAllOf(EnumValue)) + val enums = moduleCls.info.decls.filter(member => member.isAllOf(EnumValue)) for { enumValue <- enums } yield { - val fieldSym = ctx.newSymbol(clazz, enumValue.name.asTermName, EnumValue, enumValue.info) + val fieldSym = ctx.newSymbol(clazz, enumValue.name.asTermName, EnumValue | JavaStatic, enumValue.info) + fieldSym.addAnnotation(Annotations.Annotation(defn.ScalaStaticAnnot)) ValDef(fieldSym, moduleRef.select(enumValue)) } } From e05e2b7bcb46e0f94c4c8b04f3f4ff00735e818b Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 17 Jan 2020 00:25:06 +0100 Subject: [PATCH 13/13] Fix repl test --- compiler/test-resources/repl/i7410 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/test-resources/repl/i7410 b/compiler/test-resources/repl/i7410 index b2846e94baf6..1f48a4c52f05 100644 --- a/compiler/test-resources/repl/i7410 +++ b/compiler/test-resources/repl/i7410 @@ -1,4 +1,4 @@ -> enum E { case A, B, C } +scala> enum E { case A, B, C } // defined class E -> E.A +scala> E.A val res0: E = A \ No newline at end of file