Skip to content

Commit eee13a3

Browse files
committed
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.
1 parent 86d931a commit eee13a3

File tree

7 files changed

+38
-58
lines changed

7 files changed

+38
-58
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,10 @@ class Definitions {
293293
cls.info = ClassInfo(cls.owner.thisType, cls, AnyClass.typeRef :: Nil, newScope)
294294
cls.setFlag(NoInits)
295295

296-
// The companion object of a Java class doesn't really exist, `NoType` is the general
296+
// The companion object doesn't really exist, `NoType` is the general
297297
// technique to do that. Here we need to set it before completing
298298
// attempt to load Object's classfile, which causes issue #1648.
299-
val companion = JavaLangPackageVal.info.decl(cls.name.toTermName).symbol
299+
val companion = JavaLangPackageVal.info.decl(nme.Object).symbol
300300
companion.moduleClass.info = NoType // to indicate that it does not really exist
301301
companion.info = NoType // to indicate that it does not really exist
302302
completeClass(cls)
@@ -613,26 +613,6 @@ class Definitions {
613613
JavaSerializableClass.typeRef
614614
else
615615
ctx.requiredClassRef("scala.Serializable")
616-
617-
lazy val JavaEnumClass: ClassSymbol = {
618-
val cls = ctx.requiredClass("java.lang.Enum")
619-
val constr = cls.primaryConstructor
620-
val newInfo = constr.info match {
621-
case info: PolyType =>
622-
info.resType match {
623-
case meth: MethodType =>
624-
info.derivedLambdaType(
625-
resType = meth.derivedLambdaType(
626-
paramNames = Nil, paramInfos = Nil))
627-
}
628-
}
629-
constr.info = newInfo
630-
constr.termRef.recomputeDenot()
631-
cls.setFlag(NoInits | Permanent)
632-
cls
633-
}
634-
def JavaEnumType = JavaEnumClass.typeRef
635-
636616
def SerializableClass(implicit ctx: Context): ClassSymbol = SerializableType.symbol.asClass
637617
lazy val StringBuilderType: TypeRef = ctx.requiredClassRef("scala.collection.mutable.StringBuilder")
638618
def StringBuilderClass(implicit ctx: Context): ClassSymbol = StringBuilderType.symbol.asClass
@@ -693,6 +673,8 @@ class Definitions {
693673
def NoneClass(implicit ctx: Context): ClassSymbol = NoneModuleRef.symbol.moduleClass.asClass
694674
lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum")
695675
def EnumClass(implicit ctx: Context): ClassSymbol = EnumType.symbol.asClass
676+
lazy val JEnumType: TypeRef = ctx.requiredClassRef("scala.compat.JEnum")
677+
def JEnumClass(implicit ctx: Context): ClassSymbol = JEnumType.symbol.asClass
696678
lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues")
697679
def EnumValuesClass(implicit ctx: Context): ClassSymbol = EnumValuesType.symbol.asClass
698680
lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product")
@@ -1457,7 +1439,7 @@ class Definitions {
14571439
ScalaPackageClass.enter(m)
14581440

14591441
// force initialization of every symbol that is synthesized or hijacked by the compiler
1460-
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass
1442+
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
14611443

14621444
isInitialized = true
14631445
}

compiler/src/dotty/tools/dotc/transform/CompleteJavaEnums.scala

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,14 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
3434
// Because it adds additional parameters to some constructors
3535

3636
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type =
37-
if (sym.isConstructor && (
38-
sym == defn.JavaEnumClass.primaryConstructor ||
39-
derivesFromJavaEnum(sym.owner)))
40-
addConstrParams(sym.info)
37+
if (sym.isConstructor && derivesFromJEnum(sym.owner)) addConstrParams(sym.info)
4138
else tp
4239

4340
/** Is `sym` a Scala enum class that derives (directly) from `java.lang.Enum`?
4441
*/
45-
private def derivesFromJavaEnum(sym: Symbol)(implicit ctx: Context) =
42+
private def derivesFromJEnum(sym: Symbol)(implicit ctx: Context) =
4643
sym.is(Enum, butNot = Case) &&
47-
sym.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass)
44+
sym.info.parents.exists(p => p.typeSymbol == defn.JEnumClass)
4845

4946
/** Add constructor parameters `$name: String` and `$ordinal: Int` to the end of
5047
* the last parameter list of (method- or poly-) type `tp`.
@@ -100,10 +97,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
10097
*/
10198
override def transformDefDef(tree: DefDef)(implicit ctx: Context): DefDef = {
10299
val sym = tree.symbol
103-
if (sym.isConstructor && derivesFromJavaEnum(sym.owner))
100+
if (sym.isConstructor && derivesFromJEnum(sym.owner))
104101
cpy.DefDef(tree)(
105102
vparamss = tree.vparamss.init :+ (tree.vparamss.last ++ addedParams(sym, Param)))
106-
else if (sym.name == nme.DOLLAR_NEW && derivesFromJavaEnum(sym.owner.linkedClass)) {
103+
else if (sym.name == nme.DOLLAR_NEW && derivesFromJEnum(sym.owner.linkedClass)) {
107104
val Block((tdef @ TypeDef(tpnme.ANON_CLASS, templ: Template)) :: Nil, call) = tree.rhs
108105
val args = tree.vparamss.last.takeRight(2).map(param => ref(param.symbol)).reverse
109106
val templ1 = cpy.Template(templ)(
@@ -115,7 +112,8 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
115112
}
116113

117114
/** 1. If this is an enum class, add $name and $ordinal parameters to its
118-
* parameter accessors and pass them on to the java.lang.Enum constructor.
115+
* parameter accessors and pass them on to the java.lang.Enum constructor,
116+
* replacing the dummy arguments that were passed before.
119117
*
120118
* 2. If this is an anonymous class that implement a value enum case,
121119
* pass $name and $ordinal parameters to the enum superclass. The class
@@ -136,15 +134,20 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
136134
*/
137135
override def transformTemplate(templ: Template)(implicit ctx: Context): Template = {
138136
val cls = templ.symbol.owner
139-
if (derivesFromJavaEnum(cls)) {
137+
if (derivesFromJEnum(cls)) {
140138
val (params, rest) = decomposeTemplateBody(templ.body)
141139
val addedDefs = addedParams(cls, ParamAccessor)
142140
val addedSyms = addedDefs.map(_.symbol.entered)
141+
val parents1 = templ.parents.map {
142+
case app @ Apply(fn, _) if fn.symbol.owner == defn.JEnumClass =>
143+
cpy.Apply(app)(fn, addedSyms.map(ref))
144+
case p => p
145+
}
143146
cpy.Template(templ)(
144-
parents = addEnumConstrArgs(defn.JavaEnumClass, templ.parents, addedSyms.map(ref)),
147+
parents = parents1,
145148
body = params ++ addedDefs ++ rest)
146149
}
147-
else if (cls.isAnonymousClass && cls.owner.is(EnumCase) && derivesFromJavaEnum(cls.owner.owner.linkedClass)) {
150+
else if (cls.isAnonymousClass && cls.owner.is(EnumCase) && derivesFromJEnum(cls.owner.owner.linkedClass)) {
148151
def rhsOf(name: TermName) =
149152
templ.body.collect {
150153
case mdef: DefDef if mdef.name == name => mdef.rhs

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,8 +1057,8 @@ trait Checking {
10571057
(cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW)
10581058
if (!isEnumAnonCls) {
10591059
if (cdef.mods.isEnumCase) {
1060-
if (cls.derivesFrom(defn.JavaEnumClass))
1061-
ctx.error(em"paramerized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos)
1060+
if (cls.derivesFrom(defn.JEnumClass))
1061+
ctx.error(em"parameterized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos)
10621062
}
10631063
else if (cls.is(Case) || firstParent.is(Enum))
10641064
// Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -498,23 +498,9 @@ class Namer { typer: Typer =>
498498
recur(expanded(origStat))
499499
}
500500

501-
/** Determines whether this field holds an enum constant.
502-
* To qualify, the following conditions must be met:
503-
* - The field's class has the ENUM flag set
504-
* - The field's class extends java.lang.Enum
505-
* - The field has the ENUM flag set
506-
* - The field is static
507-
* - The field is stable
508-
*/
509-
def isEnumConstant(vd: ValDef)(implicit ctx: Context): Boolean = {
510-
// val ownerHasEnumFlag =
511-
// Necessary to check because scalac puts Java's static members into the companion object
512-
// while Scala's enum constants live directly in the class.
513-
// We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal
514-
// cyclic reference error. See the commit message for details.
515-
// if (ctx.compilationUnit.isJava) ctx.owner.companionClass.is(Enum) else ctx.owner.is(Enum)
516-
vd.mods.is(JavaEnumValue) // && ownerHasEnumFlag
517-
}
501+
/** Determines whether this field holds an enum constant. */
502+
def isEnumConstant(vd: ValDef)(implicit ctx: Context): Boolean =
503+
vd.mods.is(JavaEnumValue)
518504

519505
/** Add child annotation for `child` to annotations of `cls`. The annotation
520506
* is added at the correct insertion point, so that Child annotations appear

library/src/scala/compat/JEnum.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package scala.compat
2+
3+
/** A base class to be used for Scala enums that should be also exposed
4+
* as Java enums.
5+
*/
6+
abstract class JEnum[E <: java.lang.Enum[E]](
7+
name: String = throw new UnsupportedOperationException, // Compiler will pass actual values for these
8+
ordinal: Int = throw new UnsupportedOperationException) // when JEnum is inherited in an enum
9+
extends java.lang.Enum[E](name, ordinal)

tests/neg/enum-constrs.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11

2-
enum E[+T] extends java.lang.Enum[E[_]] {
2+
enum E[+T] extends compat.JEnum[E[_]] {
33
case S1, S2
44
case C() extends E[Int] // error: parameterized case is not allowed
55
}

tests/run/enum-constrs.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
enum Color extends java.lang.Enum[Color] {
1+
enum Color extends compat.JEnum[Color] {
22
case Red, Green, Blue
33
}
44

5-
enum E[+T] extends java.lang.Enum[E[_]] {
5+
enum E[+T] extends compat.JEnum[E[_]] {
66
case S1, S2
77
case C extends E[Int]
88
}
99

10-
enum Vehicle(wheels: Int) extends java.lang.Enum[Vehicle] {
10+
enum Vehicle(wheels: Int) extends compat.JEnum[Vehicle] {
1111
case Bike extends Vehicle(2)
1212
case Car extends Vehicle(4)
1313
}

0 commit comments

Comments
 (0)