Skip to content

Commit e2818f0

Browse files
Merge pull request #6624 from dotty-staging/fix-java-enums
Re-introduce java.lang.Enum constructor hijack.
2 parents 87de2d9 + 0a68c77 commit e2818f0

File tree

7 files changed

+47
-35
lines changed

7 files changed

+47
-35
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,33 @@ class Definitions {
614614
JavaSerializableClass.typeRef
615615
else
616616
ctx.requiredClassRef("scala.Serializable")
617+
618+
@threadUnsafe lazy val JavaEnumClass: ClassSymbol = {
619+
val cls = ctx.requiredClass("java.lang.Enum")
620+
cls.infoOrCompleter match {
621+
case completer: ClassfileLoader =>
622+
cls.info = new ClassfileLoader(completer.classfile) {
623+
override def complete(root: SymDenotation)(implicit ctx: Context): Unit = {
624+
super.complete(root)
625+
val constr = cls.primaryConstructor
626+
val newInfo = constr.info match {
627+
case info: PolyType =>
628+
info.resType match {
629+
case meth: MethodType =>
630+
info.derivedLambdaType(
631+
resType = meth.derivedLambdaType(
632+
paramNames = Nil, paramInfos = Nil))
633+
}
634+
}
635+
constr.info = newInfo
636+
constr.termRef.recomputeDenot()
637+
}
638+
}
639+
cls
640+
}
641+
}
642+
def JavaEnumType = JavaEnumClass.typeRef
643+
617644
def SerializableClass(implicit ctx: Context): ClassSymbol = SerializableType.symbol.asClass
618645
@threadUnsafe lazy val StringBuilderType: TypeRef = ctx.requiredClassRef("scala.collection.mutable.StringBuilder")
619646
def StringBuilderClass(implicit ctx: Context): ClassSymbol = StringBuilderType.symbol.asClass
@@ -674,8 +701,6 @@ class Definitions {
674701
def NoneClass(implicit ctx: Context): ClassSymbol = NoneModuleRef.symbol.moduleClass.asClass
675702
@threadUnsafe lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum")
676703
def EnumClass(implicit ctx: Context): ClassSymbol = EnumType.symbol.asClass
677-
@threadUnsafe lazy val JEnumType: TypeRef = ctx.requiredClassRef("scala.compat.JEnum")
678-
def JEnumClass(implicit ctx: Context): ClassSymbol = JEnumType.symbol.asClass
679704
@threadUnsafe lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues")
680705
def EnumValuesClass(implicit ctx: Context): ClassSymbol = EnumValuesType.symbol.asClass
681706
@threadUnsafe lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product")
@@ -1472,7 +1497,7 @@ class Definitions {
14721497
ScalaPackageClass.enter(m)
14731498

14741499
// force initialization of every symbol that is synthesized or hijacked by the compiler
1475-
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses()
1500+
val forced = syntheticCoreClasses ++ syntheticCoreMethods ++ ScalaValueClasses() :+ JavaEnumClass
14761501

14771502
isInitialized = true
14781503
}

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -832,8 +832,7 @@ object JavaParsers {
832832
AppliedTypeTree(javaLangDot(tpnme.Enum), List(enumType))
833833
*/
834834
val superclazz = Apply(TypeApply(
835-
Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)),
836-
List(Literal(Constant(null)),Literal(Constant(0))))
835+
Select(New(javaLangDot(tpnme.Enum)), nme.CONSTRUCTOR), List(enumType)), Nil)
837836
val enumclazz = atSpan(start, nameOffset) {
838837
TypeDef(name,
839838
makeTemplate(superclazz :: interfaces, body, List(), true)).withMods(mods | Flags.JavaEnum)

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

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

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

4144
/** Is `sym` a Scala enum class that derives (directly) from `java.lang.Enum`?
4245
*/
43-
private def derivesFromJEnum(sym: Symbol)(implicit ctx: Context) =
46+
private def derivesFromJavaEnum(sym: Symbol)(implicit ctx: Context) =
4447
sym.is(Enum, butNot = Case) &&
45-
sym.info.parents.exists(p => p.typeSymbol == defn.JEnumClass)
48+
sym.info.parents.exists(p => p.typeSymbol == defn.JavaEnumClass)
4649

4750
/** Add constructor parameters `$name: String` and `$ordinal: Int` to the end of
4851
* the last parameter list of (method- or poly-) type `tp`.
@@ -98,10 +101,10 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
98101
*/
99102
override def transformDefDef(tree: DefDef)(implicit ctx: Context): DefDef = {
100103
val sym = tree.symbol
101-
if (sym.isConstructor && derivesFromJEnum(sym.owner))
104+
if (sym.isConstructor && derivesFromJavaEnum(sym.owner))
102105
cpy.DefDef(tree)(
103106
vparamss = tree.vparamss.init :+ (tree.vparamss.last ++ addedParams(sym, Param)))
104-
else if (sym.name == nme.DOLLAR_NEW && derivesFromJEnum(sym.owner.linkedClass)) {
107+
else if (sym.name == nme.DOLLAR_NEW && derivesFromJavaEnum(sym.owner.linkedClass)) {
105108
val Block((tdef @ TypeDef(tpnme.ANON_CLASS, templ: Template)) :: Nil, call) = tree.rhs
106109
val args = tree.vparamss.last.takeRight(2).map(param => ref(param.symbol)).reverse
107110
val templ1 = cpy.Template(templ)(
@@ -113,8 +116,7 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
113116
}
114117

115118
/** 1. If this is an enum class, add $name and $ordinal parameters to its
116-
* parameter accessors and pass them on to the java.lang.Enum constructor,
117-
* replacing the dummy arguments that were passed before.
119+
* parameter accessors and pass them on to the java.lang.Enum constructor.
118120
*
119121
* 2. If this is an anonymous class that implement a value enum case,
120122
* pass $name and $ordinal parameters to the enum superclass. The class
@@ -135,20 +137,15 @@ class CompleteJavaEnums extends MiniPhase with InfoTransformer { thisPhase =>
135137
*/
136138
override def transformTemplate(templ: Template)(implicit ctx: Context): Template = {
137139
val cls = templ.symbol.owner
138-
if (derivesFromJEnum(cls)) {
140+
if (derivesFromJavaEnum(cls)) {
139141
val (params, rest) = decomposeTemplateBody(templ.body)
140142
val addedDefs = addedParams(cls, ParamAccessor)
141143
val addedSyms = addedDefs.map(_.symbol.entered)
142-
val parents1 = templ.parents.map {
143-
case app @ Apply(fn, _) if fn.symbol.owner == defn.JEnumClass =>
144-
cpy.Apply(app)(fn, addedSyms.map(ref))
145-
case p => p
146-
}
147144
cpy.Template(templ)(
148-
parents = parents1,
145+
parents = addEnumConstrArgs(defn.JavaEnumClass, templ.parents, addedSyms.map(ref)),
149146
body = params ++ addedDefs ++ rest)
150147
}
151-
else if (cls.isAnonymousClass && cls.owner.is(EnumCase) && derivesFromJEnum(cls.owner.owner.linkedClass)) {
148+
else if (cls.isAnonymousClass && cls.owner.is(EnumCase) && derivesFromJavaEnum(cls.owner.owner.linkedClass)) {
152149
def rhsOf(name: TermName) =
153150
templ.body.collect {
154151
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
@@ -1071,8 +1071,8 @@ trait Checking {
10711071
(cls.owner.flagsUNSAFE.is(Case) || cls.owner.name == nme.DOLLAR_NEW)
10721072
if (!isEnumAnonCls) {
10731073
if (cdef.mods.isEnumCase) {
1074-
if (cls.derivesFrom(defn.JEnumClass))
1075-
ctx.error(em"parameterized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos)
1074+
if (cls.derivesFrom(defn.JavaEnumClass))
1075+
ctx.error(em"paramerized case is not allowed in an enum that extends java.lang.Enum", cdef.sourcePos)
10761076
}
10771077
else if (cls.is(Case) || firstParent.is(Enum))
10781078
// Since enums are classes and Namer checks that classes don't extend multiple classes, we only check the class

library/src/scala/compat/JEnum.scala

Lines changed: 0 additions & 9 deletions
This file was deleted.

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 compat.JEnum[E[_]] {
2+
enum E[+T] extends java.lang.Enum[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 compat.JEnum[Color] {
1+
enum Color extends java.lang.Enum[Color] {
22
case Red, Green, Blue
33
}
44

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

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

0 commit comments

Comments
 (0)