From 747f9273e6a81527634325783301565b665f8020 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 15:51:44 +0200 Subject: [PATCH 01/13] Hijack java.lang.Enum constructor in Definitions --- .../dotty/tools/dotc/core/Definitions.scala | 42 ++++++++++++++----- .../dotty/tools/dotc/core/Denotations.scala | 9 +++- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2d4b2b9ce1f3..6b30727cc069 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -192,6 +192,16 @@ class Definitions { cls } + private def completeTransformedJavaClass(cls: ClassSymbol, ensureCtor: Boolean = true): ClassSymbol = { + // The companion object of a Java class doesn't really exist, `NoType` is the general + // technique to do that. Here we need to set it before completing + // attempt to load Object's classfile, which causes issue #1648. + val companion = JavaLangPackageVal.info.decl(cls.name.toTermName).symbol + companion.moduleClass.info = NoType // to indicate that it does not really exist + companion.info = NoType // to indicate that it does not really exist + completeClass(cls, ensureCtor) + } + lazy val RootClass: ClassSymbol = ctx.newPackageSymbol( NoSymbol, nme.ROOT, (root, rootcls) => ctx.base.rootLoader(root)).moduleClass.asClass lazy val RootPackage: TermSymbol = ctx.newSymbol( @@ -292,15 +302,7 @@ class Definitions { assert(!cls.isCompleted, "race for completing java.lang.Object") cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) cls.setFlag(NoInits) - - // The companion object doesn't really exist, `NoType` is the general - // technique to do that. Here we need to set it before completing - // attempt to load Object's classfile, which causes issue #1648. - val companion = JavaLangPackageVal.info.decl(nme.Object).symbol - companion.moduleClass.info = NoType // to indicate that it does not really exist - companion.info = NoType // to indicate that it does not really exist - - completeClass(cls) + completeTransformedJavaClass(cls) } def ObjectType: TypeRef = ObjectClass.typeRef @@ -614,6 +616,26 @@ class Definitions { JavaSerializableClass.typeRef else ctx.requiredClassRef("scala.Serializable") + + lazy val JavaEnumClass: ClassSymbol = { + val cls = ctx.requiredClass("java.lang.Enum") + val constr = cls.primaryConstructor + val newInfo = constr.info match { + case info: PolyType => + info.resType match { + case meth: MethodType => + info.derivedLambdaType( + resType = meth.derivedLambdaType( + paramNames = Nil, paramInfos = Nil)) + } + } + constr.info = newInfo + constr.termRef.recomputeDenot() + cls.setFlag(NoInits) + completeTransformedJavaClass(cls, ensureCtor = false) + } + def JavaEnumType = JavaEnumClass.typeRef + def SerializableClass(implicit ctx: Context): ClassSymbol = SerializableType.symbol.asClass lazy val StringBuilderType: TypeRef = ctx.requiredClassRef("scala.collection.mutable.StringBuilder") def StringBuilderClass(implicit ctx: Context): ClassSymbol = StringBuilderType.symbol.asClass @@ -1438,7 +1460,7 @@ class Definitions { ScalaPackageClass.enter(m) // force initialization of every symbol that is synthesized or hijacked by the compiler - val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() + val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass isInitialized = true } diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index e992a53a4d1b..32655e764fe4 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -814,8 +814,13 @@ object Denotations { def invalidateInheritedInfo(): Unit = () private def updateValidity()(implicit ctx: Context): this.type = { - assert(ctx.runId >= validFor.runId || ctx.settings.YtestPickler.value, // mixing test pickler with debug printing can travel back in time - s"denotation $this invalid in run ${ctx.runId}. ValidFor: $validFor") + assert( + ctx.runId >= validFor.runId || + ctx.settings.YtestPickler.value || // mixing test pickler with debug printing can travel back in time + symbol.isContainedIn(defn.JavaEnumClass) || // the java.lang.Enum constructor highjacking leads to backwards time travel... + symbol.is(Package), // ... which also means packages can travel backwards in time. + + s"denotation $this invalid in run ${ctx.runId}. ValidFor: $validFor") var d: SingleDenotation = this do { d.validFor = Period(ctx.period.runId, d.validFor.firstPhaseId, d.validFor.lastPhaseId) From 6d59b1cc8f4efdebcc6ae0ad64e31dfdb9ec48f3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 21:32:18 +0200 Subject: [PATCH 02/13] Miniphase for adding constructor parameters to Java enums Miniphase for adding constructor parameters if a Scala enum extends a Java enum. --- compiler/src/dotty/tools/dotc/Compiler.scala | 3 +- .../src/dotty/tools/dotc/ast/TreeInfo.scala | 9 + .../tools/dotc/core/tasty/TreePickler.scala | 7 +- .../dotc/transform/CompleteJavaEnums.scala | 157 ++++++++++++++++++ 4 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 6f8aed16fdc3..53d51077e967 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -56,7 +56,8 @@ class Compiler { List(new FirstTransform, // Some transformations to put trees into a canonical form 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 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 ElimRepeated, // Rewrite vararg parameters and arguments new ExpandSAMs, // Expand single abstract method closures to anonymous classes diff --git a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala index f6622eedd04b..4a363a14e86a 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -596,6 +596,15 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => loop(tree, Nil, Nil) } + /** Decompose a template body into parameters and other statements */ + def decomposeTemplateBody(body: List[Tree])(implicit ctx: Context): (List[Tree], List[Tree]) = + body.partition { + case stat: TypeDef => stat.symbol is Flags.Param + case stat: ValOrDefDef => + stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter + case _ => false + } + /** An extractor for closures, either contained in a block or standalone. */ object closure { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index dbb24b42076c..3078c10b1d83 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -509,12 +509,7 @@ class TreePickler(pickler: TastyPickler) { case tree: Template => registerDef(tree.symbol) writeByte(TEMPLATE) - val (params, rest) = tree.body partition { - case stat: TypeDef => stat.symbol is Flags.Param - case stat: ValOrDefDef => - stat.symbol.is(Flags.ParamAccessor) && !stat.symbol.isSetter - case _ => false - } + val (params, rest) = decomposeTemplateBody(tree.body) withLength { pickleParams(params) tree.parents.foreach(pickleTree) diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala new file mode 100644 index 000000000000..3a117ccace4a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -0,0 +1,157 @@ +package dotty.tools.dotc +package transform + +import core._ +import Names._ +import StdNames.{nme, tpnme} +import Types._ +import dotty.tools.dotc.transform.MegaPhase._ +import Flags._ +import Contexts.Context +import Symbols._ +import Constants._ +import Decorators._ +import DenotTransformers._ + +object CompleteJavaEnums { + val name: String = "completeJavaEnums" + + private val nameParamName: TermName = "$name".toTermName + private val ordinalParamName: TermName = "$ordinal".toTermName +} + +/** For Scala enums that inherit from java.lang.Enum: + * Add constructor parameters for `name` and `ordinal` to pass from each + * case to the java.lang.Enum class. + */ +class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => + import CompleteJavaEnums._ + import ast.tpd._ + + override def phaseName: String = CompleteJavaEnums.name + + override def relaxedTypingInGroup: Boolean = true + // Because it adds additional parameters to some constructors + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = + if (sym.isConstructor && ( + sym == defn.JavaEnumClass.primaryConstructor || + derivesFromJavaEnum(sym.owner))) + addConstrParams(sym.info) + else tp + + /** Is `sym` a Scala enum class that derives from `java.lang.Enum`? + */ + private def derivesFromJavaEnum(sym: Symbol)(implicit ctx: Context) = + sym.is(Enum, butNot = Case) && sym.derivesFrom(defn.JavaEnumClass) + + /** Add constructor parameters `$name: String` and `$ordinal: Int` to the end of + * the last parameter list of (method- or poly-) type `tp`. + */ + private def addConstrParams(tp: Type)(implicit ctx: Context): Type = tp match { + case tp: PolyType => + tp.derivedLambdaType(resType = addConstrParams(tp.resType)) + case tp: MethodType => + tp.resType match { + case restpe: MethodType => + tp.derivedLambdaType(resType = addConstrParams(restpe)) + case _ => + tp.derivedLambdaType( + paramNames = tp.paramNames ++ List(nameParamName, ordinalParamName), + paramInfos = tp.paramInfos ++ List(defn.StringType, defn.IntType)) + } + } + + /** The list of parameter definitions `$name: String, $ordinal: Int`, in given `owner` + * with given flags (either `Param` or `ParamAccessor`) + */ + private def addedParams(owner: Symbol, flag: FlagSet)(implicit ctx: Context): List[ValDef] = { + val nameParam = ctx.newSymbol(owner, nameParamName, flag | Synthetic, defn.StringType, coord = owner.span) + val ordinalParam = ctx.newSymbol(owner, ordinalParamName, flag | Synthetic, defn.IntType, coord = owner.span) + List(ValDef(nameParam), ValDef(ordinalParam)) + } + + /** Add arguments `args` to the parent constructor application in `parents` that invokes + * a constructor of `targetCls`, + */ + private def addEnumConstrArgs(targetCls: Symbol, parents: List[Tree], args: List[Tree])(implicit ctx: Context): List[Tree] = + parents.map { + case app @ Apply(fn, args0) if fn.symbol.owner == targetCls => cpy.Apply(app)(fn, args0 ++ args) + case p => p + } + + /** 1. If this is a constructor of a enum class that extends, add $name and $ordinal parameters to it. + * + * 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) = { + * 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) { ... } + * new $anon + * } + */ + override def transformDefDef(tree: DefDef)(implicit ctx: Context): DefDef = { + val sym = tree.symbol + if (sym.isConstructor && derivesFromJavaEnum(sym.owner)) + cpy.DefDef(tree)( + vparamss = tree.vparamss.init :+ (tree.vparamss.last ++ addedParams(sym, Param))) + else if (sym.name == nme.DOLLAR_NEW && derivesFromJavaEnum(sym.owner.linkedClass)) { + 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)( + parents = addEnumConstrArgs(sym.owner.linkedClass, templ.parents, args)) + cpy.DefDef(tree)( + rhs = cpy.Block(tree.rhs)(cpy.TypeDef(tdef)(tdef.name, templ1) :: Nil, call)) + } + else tree + } + + /** 1. If this is an enum class, add $name and $ordinal parameters to its + * parameter accessors and pass them on to the java.lang.Enum constructor. + * + * 2. If this is an anonymous class that implement a value enum case, + * pass $name and $ordinal parameters to the enum superclass. The class + * looks like this: + * + * class $anon extends E(...) { + * ... + * def enumTag = N + * def toString = S + * ... + * } + * + * After the transform it is expanded to + * + * class $anon extends E(..., N, S) { + * "same as before" + * } + */ + override def transformTemplate(templ: Template)(implicit ctx: Context): Template = { + val cls = templ.symbol.owner + if (derivesFromJavaEnum(cls)) { + val (params, rest) = decomposeTemplateBody(templ.body) + val addedDefs = addedParams(cls, ParamAccessor) + val addedSyms = addedDefs.map(_.symbol.entered) + cpy.Template(templ)( + 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)) { + def rhsOf(name: TermName) = + templ.body.collect { + case mdef: DefDef if mdef.name == name => mdef.rhs + }.head + val args = List(rhsOf(nme.toString_), rhsOf(nme.enumTag)) + cpy.Template(templ)( + parents = addEnumConstrArgs(cls.owner.owner.linkedClass, templ.parents, args)) + } + else templ + } +} From 4c800e2119f98d9f4deb3a389d4ba59812904b51 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 21:32:50 +0200 Subject: [PATCH 03/13] Also print unique ids for symbols with meaningless names --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 -- .../src/dotty/tools/dotc/transform/CompleteJavaEnums.scala | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5656baacaaf1..83e3e7ab7356 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -850,8 +850,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { nameString(sym.owner.name) else if (sym.is(ModuleClass)) nameString(sym.name.stripModuleClassSuffix) - else if (hasMeaninglessName(sym)) - simpleNameString(sym.owner) else nameString(sym) (keywordText(kindString(sym)) ~~ { diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index 3a117ccace4a..9174ed825a0b 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -40,10 +40,11 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => addConstrParams(sym.info) else tp - /** Is `sym` a Scala enum class that derives from `java.lang.Enum`? + /** 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.derivesFrom(defn.JavaEnumClass) + 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`. From ff6b5d48493112720e3e58aa378a7b5e4e613ce1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 21:49:10 +0200 Subject: [PATCH 04/13] Check that Java sub-enums do not have parameterized enum cases --- .../src/dotty/tools/dotc/typer/Checking.scala | 22 ++++++++++++------- tests/neg/enum-constrs.scala | 5 +++++ tests/run/enum-constrs.scala | 13 +++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 tests/neg/enum-constrs.scala create mode 100644 tests/run/enum-constrs.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 7e94b41b3452..ef17939c3119 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1046,20 +1046,26 @@ trait Checking { ctx.error(em"$what $msg", posd.sourcePos) } - /** Check that all case classes that extend `scala.Enum` are `enum` cases */ + /** 1. Check that all case classes that extend `scala.Enum` are `enum` cases + * 2. Check that case class `enum` cases do not extend java.lang.Enum. + */ def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(implicit ctx: Context): Unit = { import untpd.modsDeco def isEnumAnonCls = cls.isAnonymousClass && cls.owner.isTerm && (cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW) - if (!cdef.mods.isEnumCase && !isEnumAnonCls) { - // Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class - // parent. - // - // Unlike firstParent.derivesFrom(defn.EnumClass), this test allows inheriting from `Enum` by hand; - // see enum-List-control.scala. - if (cls.is(Case) || firstParent.is(Enum)) + if (!isEnumAnonCls) { + if (cdef.mods.isEnumCase) { + if (cls.derivesFrom(defn.JavaEnumClass)) + ctx.error(em"paramerized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos) + } + else if (cls.is(Case) || firstParent.is(Enum)) + // Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class + // parent. + // + // Unlike firstParent.derivesFrom(defn.EnumClass), this test allows inheriting from `Enum` by hand; + // see enum-List-control.scala. ctx.error(ClassCannotExtendEnum(cls, firstParent), cdef.sourcePos) } } diff --git a/tests/neg/enum-constrs.scala b/tests/neg/enum-constrs.scala new file mode 100644 index 000000000000..13c39628044d --- /dev/null +++ b/tests/neg/enum-constrs.scala @@ -0,0 +1,5 @@ + +enum E[+T] extends java.lang.Enum[E[_]] { + case S1, S2 + case C() extends E[Int] // error: parameterized case is not allowed +} diff --git a/tests/run/enum-constrs.scala b/tests/run/enum-constrs.scala new file mode 100644 index 000000000000..aa3d7a8d60ec --- /dev/null +++ b/tests/run/enum-constrs.scala @@ -0,0 +1,13 @@ +enum Color extends java.lang.Enum[Color] { + case Red, Green, Blue +} + +enum E[+T] extends java.lang.Enum[E[_]] { + case S1, S2 + case C extends E[Int] +} + +enum Vehicle(wheels: Int) extends java.lang.Enum[Vehicle] { + case Bike extends Vehicle(2) + case Car extends Vehicle(4) +} \ No newline at end of file From 39e1374f08c3bb6fe6d5f8ac6ee6532c5dacfdc4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 22:13:13 +0200 Subject: [PATCH 05/13] Don't pass dummy arguments to java.lang.Enum when parsing Java Since java.lang.Enum now pretends ot have an empty constructor, Java trees should not pass any arguments to it either. --- compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 6601d74f2c81..692d99b053b0 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -832,8 +832,7 @@ object JavaParsers { AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType)) */ val superclazz = Apply(TypeApply( - Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), - List(Literal(Constant(null)),Literal(Constant(0)))) + Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), Nil) val enumclazz = atSpan(start, nameOffset) { TypeDef(name, makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.JavaEnum) From bfb1e1fd9f565f1818896ea90e01fa40e6172128 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 22:21:12 +0200 Subject: [PATCH 06/13] Revert special handling of java.lang.Enum's companion object Unlike Object, which is the other class we fiddle with, java.lang.Enum really has a companion object. So we should leave it as it is. --- .../dotty/tools/dotc/core/Definitions.scala | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 6b30727cc069..015565a27af1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -192,16 +192,6 @@ class Definitions { cls } - private def completeTransformedJavaClass(cls: ClassSymbol, ensureCtor: Boolean = true): ClassSymbol = { - // The companion object of a Java class doesn't really exist, `NoType` is the general - // technique to do that. Here we need to set it before completing - // attempt to load Object's classfile, which causes issue #1648. - val companion = JavaLangPackageVal.info.decl(cls.name.toTermName).symbol - companion.moduleClass.info = NoType // to indicate that it does not really exist - companion.info = NoType // to indicate that it does not really exist - completeClass(cls, ensureCtor) - } - lazy val RootClass: ClassSymbol = ctx.newPackageSymbol( NoSymbol, nme.ROOT, (root, rootcls) => ctx.base.rootLoader(root)).moduleClass.asClass lazy val RootPackage: TermSymbol = ctx.newSymbol( @@ -302,7 +292,14 @@ class Definitions { assert(!cls.isCompleted, "race for completing java.lang.Object") cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) cls.setFlag(NoInits) - completeTransformedJavaClass(cls) + + // The companion object of a Java class doesn't really exist, `NoType` is the general + // technique to do that. Here we need to set it before completing + // attempt to load Object's classfile, which causes issue #1648. + val companion = JavaLangPackageVal.info.decl(cls.name.toTermName).symbol + companion.moduleClass.info = NoType // to indicate that it does not really exist + companion.info = NoType // to indicate that it does not really exist + completeClass(cls) } def ObjectType: TypeRef = ObjectClass.typeRef @@ -632,7 +629,7 @@ class Definitions { constr.info = newInfo constr.termRef.recomputeDenot() cls.setFlag(NoInits) - completeTransformedJavaClass(cls, ensureCtor = false) + cls } def JavaEnumType = JavaEnumClass.typeRef From ed35468b64e7d776d6f6177620ee3ac4b7534cb6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 22:35:02 +0200 Subject: [PATCH 07/13] Fix run test --- tests/run/enum-constrs.check | 3 +++ tests/run/enum-constrs.scala | 6 ++++++ 2 files changed, 9 insertions(+) create mode 100644 tests/run/enum-constrs.check diff --git a/tests/run/enum-constrs.check b/tests/run/enum-constrs.check new file mode 100644 index 000000000000..668ec8702ae8 --- /dev/null +++ b/tests/run/enum-constrs.check @@ -0,0 +1,3 @@ +Red +S1 +Car diff --git a/tests/run/enum-constrs.scala b/tests/run/enum-constrs.scala index aa3d7a8d60ec..1418f39b99b2 100644 --- a/tests/run/enum-constrs.scala +++ b/tests/run/enum-constrs.scala @@ -10,4 +10,10 @@ enum E[+T] extends java.lang.Enum[E[_]] { enum Vehicle(wheels: Int) extends java.lang.Enum[Vehicle] { case Bike extends Vehicle(2) case Car extends Vehicle(4) +} + +object Test extends App { + println(Color.Red) + println(E.S1) + println(Vehicle.Car) } \ No newline at end of file From 1197d9600f3e53a634bdf02a92b00823bd9cc08d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Jun 2019 22:59:49 +0200 Subject: [PATCH 08/13] Make java.lang.Enum a Permanent symbol This avoids extensive special casing in Denotations#updateValidity --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 2 +- compiler/src/dotty/tools/dotc/core/Denotations.scala | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 015565a27af1..e3db043dc68b 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -628,7 +628,7 @@ class Definitions { } constr.info = newInfo constr.termRef.recomputeDenot() - cls.setFlag(NoInits) + cls.setFlag(NoInits | Permanent) cls } def JavaEnumType = JavaEnumClass.typeRef diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 32655e764fe4..17d22d23803c 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -816,10 +816,8 @@ object Denotations { private def updateValidity()(implicit ctx: Context): this.type = { assert( ctx.runId >= validFor.runId || - ctx.settings.YtestPickler.value || // mixing test pickler with debug printing can travel back in time - symbol.isContainedIn(defn.JavaEnumClass) || // the java.lang.Enum constructor highjacking leads to backwards time travel... - symbol.is(Package), // ... which also means packages can travel backwards in time. - + ctx.settings.YtestPickler.value || // mixing test pickler with debug printing can travel back in time + symbol.is(Permanent), // Permanent symbols are valid in all runIds s"denotation $this invalid in run ${ctx.runId}. ValidFor: $validFor") var d: SingleDenotation = this do { From 94e40aee4a70091aaff5f2c4552a9a70e6dbc4d9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Jun 2019 00:12:41 +0200 Subject: [PATCH 09/13] Make TastyFormat recognize Enum as a modifier tag. --- compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index cf3352ac6c4c..efb31dfab3ef 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -490,6 +490,7 @@ object TastyFormat { | STATIC | OBJECT | TRAIT + | ENUM | LOCAL | SYNTHETIC | ARTIFACT From 430cac550656263e9d4f5f064a3b817a5eb4183f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Jun 2019 08:29:45 +0200 Subject: [PATCH 10/13] Fix: Also print unique ids for symbols with meaningless names --- compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 83e3e7ab7356..d77d4a0bf25d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -850,6 +850,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { nameString(sym.owner.name) else if (sym.is(ModuleClass)) nameString(sym.name.stripModuleClassSuffix) + else if (hasMeaninglessName(sym)) + simpleNameString(sym.owner) + idString(sym) else nameString(sym) (keywordText(kindString(sym)) ~~ { From 86d931a4943e9895fcebb160fa10888687043f8a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Jun 2019 12:15:12 +0200 Subject: [PATCH 11/13] Make sure all modifier tags are recognized as such --- .../tools/dotc/core/tasty/TreePickler.scala | 74 ++++++++++--------- 1 file changed, 39 insertions(+), 35 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 3078c10b1d83..1ed294163ea6 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -630,44 +630,48 @@ class TreePickler(pickler: TastyPickler) { def pickleFlags(flags: FlagSet, isTerm: Boolean)(implicit ctx: Context): Unit = { import Flags._ - if (flags is Private) writeByte(PRIVATE) - if (flags is Protected) writeByte(PROTECTED) - if (flags.is(Final, butNot = Module)) writeByte(FINAL) - if (flags is Case) writeByte(CASE) - if (flags is Override) writeByte(OVERRIDE) - if (flags is Inline) writeByte(INLINE) - if (flags is InlineProxy) writeByte(INLINEPROXY) - if (flags is Macro) writeByte(MACRO) - if (flags is JavaStatic) writeByte(STATIC) - if (flags is Module) writeByte(OBJECT) - if (flags is Enum) writeByte(ENUM) - if (flags is Local) writeByte(LOCAL) - if (flags is Synthetic) writeByte(SYNTHETIC) - if (flags is Artifact) writeByte(ARTIFACT) - if (flags is Scala2x) writeByte(SCALA2X) + def writeModTag(tag: Int) = { + assert(isModifierTag(tag)) + writeByte(tag) + } + if (flags is Private) writeModTag(PRIVATE) + if (flags is Protected) writeModTag(PROTECTED) + if (flags.is(Final, butNot = Module)) writeModTag(FINAL) + if (flags is Case) writeModTag(CASE) + if (flags is Override) writeModTag(OVERRIDE) + if (flags is Inline) writeModTag(INLINE) + if (flags is InlineProxy) writeModTag(INLINEPROXY) + if (flags is Macro) writeModTag(MACRO) + if (flags is JavaStatic) writeModTag(STATIC) + if (flags is Module) writeModTag(OBJECT) + if (flags is Enum) writeModTag(ENUM) + if (flags is Local) writeModTag(LOCAL) + if (flags is Synthetic) writeModTag(SYNTHETIC) + if (flags is Artifact) writeModTag(ARTIFACT) + if (flags is Scala2x) writeModTag(SCALA2X) if (isTerm) { - if (flags is Implicit) writeByte(IMPLICIT) - if (flags is Implied) writeByte(IMPLIED) - if (flags is Erased) writeByte(ERASED) - if (flags.is(Lazy, butNot = Module)) writeByte(LAZY) - if (flags is AbsOverride) { writeByte(ABSTRACT); writeByte(OVERRIDE) } - if (flags is Mutable) writeByte(MUTABLE) - if (flags is Accessor) writeByte(FIELDaccessor) - if (flags is CaseAccessor) writeByte(CASEaccessor) - if (flags is DefaultParameterized) writeByte(DEFAULTparameterized) - if (flags is StableRealizable) writeByte(STABLE) - if (flags is Extension) writeByte(EXTENSION) - if (flags is Given) writeByte(GIVEN) - if (flags is ParamAccessor) writeByte(PARAMsetter) - if (flags is Exported) writeByte(EXPORTED) + if (flags is Implicit) writeModTag(IMPLICIT) + if (flags is Implied) writeModTag(IMPLIED) + if (flags is Erased) writeModTag(ERASED) + if (flags.is(Lazy, butNot = Module)) writeModTag(LAZY) + if (flags is AbsOverride) { writeModTag(ABSTRACT); writeModTag(OVERRIDE) } + if (flags is Mutable) writeModTag(MUTABLE) + if (flags is Accessor) writeModTag(FIELDaccessor) + if (flags is CaseAccessor) writeModTag(CASEaccessor) + if (flags is DefaultParameterized) writeModTag(DEFAULTparameterized) + if (flags is StableRealizable) writeModTag(STABLE) + if (flags is Extension) writeModTag(EXTENSION) + if (flags is Given) writeModTag(GIVEN) + if (flags is ParamAccessor) writeModTag(PARAMsetter) + if (flags is Exported) writeModTag(EXPORTED) assert(!(flags is Label)) } else { - if (flags is Sealed) writeByte(SEALED) - if (flags is Abstract) writeByte(ABSTRACT) - if (flags is Trait) writeByte(TRAIT) - if (flags is Covariant) writeByte(COVARIANT) - if (flags is Contravariant) writeByte(CONTRAVARIANT) - if (flags is Opaque) writeByte(OPAQUE) + if (flags is Sealed) writeModTag(SEALED) + if (flags is Abstract) writeModTag(ABSTRACT) + if (flags is Trait) writeModTag(TRAIT) + if (flags is Covariant) writeModTag(COVARIANT) + if (flags is Contravariant) writeModTag(CONTRAVARIANT) + if (flags is Opaque) writeModTag(OPAQUE) } } From eee13a38133d3d2d521d01e56faf09e324b60054 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Jun 2019 12:39:42 +0200 Subject: [PATCH 12/13] Use scala.compat.JEnum instead of java.lang.Enum Use scala.compat.JEnum instead of java.lang.Enum to publish a Scala enum as a Java enum. This avoids having to hijack java.lang.Enums constructor. The latter leads to problems when compiling the standard 2.13 standard library. --- .../dotty/tools/dotc/core/Definitions.scala | 28 ++++--------------- .../dotc/transform/CompleteJavaEnums.scala | 27 ++++++++++-------- .../src/dotty/tools/dotc/typer/Checking.scala | 4 +-- .../src/dotty/tools/dotc/typer/Namer.scala | 20 ++----------- library/src/scala/compat/JEnum.scala | 9 ++++++ tests/neg/enum-constrs.scala | 2 +- tests/run/enum-constrs.scala | 6 ++-- 7 files changed, 38 insertions(+), 58 deletions(-) create mode 100644 library/src/scala/compat/JEnum.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index e3db043dc68b..95320e5e91f1 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -293,10 +293,10 @@ class Definitions { cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope) cls.setFlag(NoInits) - // The companion object of a Java class doesn't really exist, `NoType` is the general + // The companion object doesn't really exist, `NoType` is the general // technique to do that. Here we need to set it before completing // attempt to load Object's classfile, which causes issue #1648. - val companion = JavaLangPackageVal.info.decl(cls.name.toTermName).symbol + val companion = JavaLangPackageVal.info.decl(nme.Object).symbol companion.moduleClass.info = NoType // to indicate that it does not really exist companion.info = NoType // to indicate that it does not really exist completeClass(cls) @@ -613,26 +613,6 @@ class Definitions { JavaSerializableClass.typeRef else ctx.requiredClassRef("scala.Serializable") - - lazy val JavaEnumClass: ClassSymbol = { - val cls = ctx.requiredClass("java.lang.Enum") - val constr = cls.primaryConstructor - val newInfo = constr.info match { - case info: PolyType => - info.resType match { - case meth: MethodType => - info.derivedLambdaType( - resType = meth.derivedLambdaType( - paramNames = Nil, paramInfos = Nil)) - } - } - constr.info = newInfo - constr.termRef.recomputeDenot() - cls.setFlag(NoInits | Permanent) - cls - } - def JavaEnumType = JavaEnumClass.typeRef - def SerializableClass(implicit ctx: Context): ClassSymbol = SerializableType.symbol.asClass lazy val StringBuilderType: TypeRef = ctx.requiredClassRef("scala.collection.mutable.StringBuilder") def StringBuilderClass(implicit ctx: Context): ClassSymbol = StringBuilderType.symbol.asClass @@ -693,6 +673,8 @@ class Definitions { def NoneClass(implicit ctx: Context): ClassSymbol = NoneModuleRef.symbol.moduleClass.asClass lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum") def EnumClass(implicit ctx: Context): ClassSymbol = EnumType.symbol.asClass + lazy val JEnumType: TypeRef = ctx.requiredClassRef("scala.compat.JEnum") + def JEnumClass(implicit ctx: Context): ClassSymbol = JEnumType.symbol.asClass lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues") def EnumValuesClass(implicit ctx: Context): ClassSymbol = EnumValuesType.symbol.asClass lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product") @@ -1457,7 +1439,7 @@ class Definitions { ScalaPackageClass.enter(m) // force initialization of every symbol that is synthesized or hijacked by the compiler - val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass + val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() isInitialized = true } diff --git a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala index 9174ed825a0b..14e1bd065f39 100644 --- a/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala +++ b/compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala @@ -34,17 +34,14 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => // Because it adds additional parameters to some constructors def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - if (sym.isConstructor && ( - sym == defn.JavaEnumClass.primaryConstructor || - derivesFromJavaEnum(sym.owner))) - addConstrParams(sym.info) + if (sym.isConstructor && derivesFromJEnum(sym.owner)) 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) = + private def derivesFromJEnum(sym: Symbol)(implicit ctx: Context) = sym.is(Enum, butNot = Case) && - sym.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass) + sym.info.parents.exists(p => p.typeSymbol == defn.JEnumClass) /** Add constructor parameters `$name: String` and `$ordinal: Int` to the end of * the last parameter list of (method- or poly-) type `tp`. @@ -100,10 +97,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 && derivesFromJEnum(sym.owner)) 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 && derivesFromJEnum(sym.owner.linkedClass)) { 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)( @@ -115,7 +112,8 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase => } /** 1. If this is an enum class, add $name and $ordinal parameters to its - * parameter accessors and pass them on to the java.lang.Enum constructor. + * parameter accessors and pass them on to the java.lang.Enum constructor, + * replacing the dummy arguments that were passed before. * * 2. If this is an anonymous class that implement a value enum case, * pass $name and $ordinal parameters to the enum superclass. The class @@ -136,15 +134,20 @@ 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 (derivesFromJEnum(cls)) { val (params, rest) = decomposeTemplateBody(templ.body) val addedDefs = addedParams(cls, ParamAccessor) val addedSyms = addedDefs.map(_.symbol.entered) + val parents1 = templ.parents.map { + case app @ Apply(fn, _) if fn.symbol.owner == defn.JEnumClass => + cpy.Apply(app)(fn, addedSyms.map(ref)) + case p => p + } cpy.Template(templ)( - parents = addEnumConstrArgs(defn.JavaEnumClass, templ.parents, addedSyms.map(ref)), + parents = parents1, 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) && derivesFromJEnum(cls.owner.owner.linkedClass)) { def rhsOf(name: TermName) = templ.body.collect { case mdef: DefDef if mdef.name == name => mdef.rhs diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index ef17939c3119..629ce7b953b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1057,8 +1057,8 @@ trait Checking { (cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW) if (!isEnumAnonCls) { if (cdef.mods.isEnumCase) { - if (cls.derivesFrom(defn.JavaEnumClass)) - ctx.error(em"paramerized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos) + if (cls.derivesFrom(defn.JEnumClass)) + ctx.error(em"parameterized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos) } else if (cls.is(Case) || firstParent.is(Enum)) // Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 41fde927164e..d7ea2549774d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -498,23 +498,9 @@ class Namer { typer: Typer => recur(expanded(origStat)) } - /** Determines whether this field holds an enum constant. - * To qualify, the following conditions must be met: - * - The field's class has the ENUM flag set - * - The field's class extends java.lang.Enum - * - The field has the ENUM flag set - * - The field is static - * - The field is stable - */ - def isEnumConstant(vd: ValDef)(implicit ctx: Context): Boolean = { - // val ownerHasEnumFlag = - // Necessary to check because scalac puts Java's static members into the companion object - // while Scala's enum constants live directly in the class. - // We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal - // cyclic reference error. See the commit message for details. - // if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum) - vd.mods.is(JavaEnumValue) // && ownerHasEnumFlag - } + /** Determines whether this field holds an enum constant. */ + def isEnumConstant(vd: ValDef)(implicit ctx: Context): Boolean = + vd.mods.is(JavaEnumValue) /** Add child annotation for `child` to annotations of `cls`. The annotation * is added at the correct insertion point, so that Child annotations appear diff --git a/library/src/scala/compat/JEnum.scala b/library/src/scala/compat/JEnum.scala new file mode 100644 index 000000000000..d2e696534d4b --- /dev/null +++ b/library/src/scala/compat/JEnum.scala @@ -0,0 +1,9 @@ +package scala.compat + +/** A base class to be used for Scala enums that should be also exposed + * as Java enums. + */ +abstract class JEnum[E <: java.lang.Enum[E]]( + name: String = throw new UnsupportedOperationException, // Compiler will pass actual values for these + ordinal: Int = throw new UnsupportedOperationException) // when JEnum is inherited in an enum +extends java.lang.Enum[E](name, ordinal) \ No newline at end of file diff --git a/tests/neg/enum-constrs.scala b/tests/neg/enum-constrs.scala index 13c39628044d..5cc33fae7de5 100644 --- a/tests/neg/enum-constrs.scala +++ b/tests/neg/enum-constrs.scala @@ -1,5 +1,5 @@ -enum E[+T] extends java.lang.Enum[E[_]] { +enum E[+T] extends compat.JEnum[E[_]] { case S1, S2 case C() extends E[Int] // error: parameterized case is not allowed } diff --git a/tests/run/enum-constrs.scala b/tests/run/enum-constrs.scala index 1418f39b99b2..b1988f91771c 100644 --- a/tests/run/enum-constrs.scala +++ b/tests/run/enum-constrs.scala @@ -1,13 +1,13 @@ -enum Color extends java.lang.Enum[Color] { +enum Color extends compat.JEnum[Color] { case Red, Green, Blue } -enum E[+T] extends java.lang.Enum[E[_]] { +enum E[+T] extends compat.JEnum[E[_]] { case S1, S2 case C extends E[Int] } -enum Vehicle(wheels: Int) extends java.lang.Enum[Vehicle] { +enum Vehicle(wheels: Int) extends compat.JEnum[Vehicle] { case Bike extends Vehicle(2) case Car extends Vehicle(4) } From a8474fd9e096ead6a08b62fd62c8516d737b6986 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 5 Jun 2019 13:33:28 +0200 Subject: [PATCH 13/13] Don't pass dummy arguments to java.lang.Enum when parsing Java Since java.lang.Enum now pretends ot have an empty constructor, Java trees should not pass any arguments to it either. (reverted from commit 39e1374f08c3bb6fe6d5f8ac6ee6532c5dacfdc4) --- compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 692d99b053b0..6601d74f2c81 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -832,7 +832,8 @@ object JavaParsers { AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType)) */ val superclazz = Apply(TypeApply( - Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), Nil) + Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), + List(Literal(Constant(null)),Literal(Constant(0)))) val enumclazz = atSpan(start, nameOffset) { TypeDef(name, makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.JavaEnum)