Skip to content

Commit 2d25fd6

Browse files
committed
Re-introduce java.lang.Enum constructor hijack.
But this time do it lazily, by installing a special completer.
1 parent 33ad06b commit 2d25fd6

File tree

8 files changed

+64
-38
lines changed

8 files changed

+64
-38
lines changed

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

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,33 @@ 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+
cls.infoOrCompleter match {
620+
case completer: ClassfileLoader =>
621+
cls.info = new ClassfileLoader(completer.classfile) {
622+
override def complete(root: SymDenotation)(implicit ctx: Context): Unit = {
623+
super.complete(root)
624+
val constr = cls.primaryConstructor
625+
val newInfo = constr.info match {
626+
case info: PolyType =>
627+
info.resType match {
628+
case meth: MethodType =>
629+
info.derivedLambdaType(
630+
resType = meth.derivedLambdaType(
631+
paramNames = Nil, paramInfos = Nil))
632+
}
633+
}
634+
constr.info = newInfo
635+
constr.termRef.recomputeDenot()
636+
}
637+
}
638+
cls
639+
}
640+
}
641+
def JavaEnumType = JavaEnumClass.typeRef
642+
616643
def SerializableClass(implicit ctx: Context): ClassSymbol = SerializableType.symbol.asClass
617644
lazy val StringBuilderType: TypeRef = ctx.requiredClassRef("scala.collection.mutable.StringBuilder")
618645
def StringBuilderClass(implicit ctx: Context): ClassSymbol = StringBuilderType.symbol.asClass
@@ -673,8 +700,6 @@ class Definitions {
673700
def NoneClass(implicit ctx: Context): ClassSymbol = NoneModuleRef.symbol.moduleClass.asClass
674701
lazy val EnumType: TypeRef = ctx.requiredClassRef("scala.Enum")
675702
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
678703
lazy val EnumValuesType: TypeRef = ctx.requiredClassRef("scala.runtime.EnumValues")
679704
def EnumValuesClass(implicit ctx: Context): ClassSymbol = EnumValuesType.symbol.asClass
680705
lazy val ProductType: TypeRef = ctx.requiredClassRef("scala.Product")
@@ -1457,7 +1482,7 @@ class Definitions {
14571482
ScalaPackageClass.enter(m)
14581483

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

14621487
isInitialized = true
14631488
}

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

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

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

501-
/** Determines whether this field holds an enum constant. */
502-
def isEnumConstant(vd: ValDef)(implicit ctx: Context): Boolean =
503-
vd.mods.is(JavaEnumValue)
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+
}
504518

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

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)