Skip to content

Commit 8392705

Browse files
committed
fix #9873: deprecate scala.Enum, no longer use it as parent of enums
this commit introduces the concept that some desugaring expansion can occur after parents of the current class are known, i.e. methods that contain no references like ordinal and enumLabel.
1 parent 246fc89 commit 8392705

File tree

12 files changed

+48
-72
lines changed

12 files changed

+48
-72
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ object desugar {
403403
val isCaseObject = mods.is(Case) && isObject
404404
val isEnum = mods.isEnumClass && !mods.is(Module)
405405
def isEnumCase = mods.isEnumCase
406+
def isNonEnumCase = !isEnumCase && (isCaseClass || isCaseObject)
406407
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
407408
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.
408409

@@ -483,7 +484,8 @@ object desugar {
483484
val enumCompanionRef = TermRefTree()
484485
val enumImport =
485486
Import(enumCompanionRef, enumCases.flatMap(caseIds).map(ImportSelector(_)))
486-
(enumImport :: enumStats, enumCases, enumCompanionRef)
487+
val enumSpecMethods = EnumGetters()
488+
(enumImport :: enumSpecMethods :: enumStats, enumCases, enumCompanionRef)
487489
}
488490
else (stats, Nil, EmptyTree)
489491
}
@@ -621,10 +623,8 @@ object desugar {
621623
var parents1 = parents
622624
if (isEnumCase && parents.isEmpty)
623625
parents1 = enumClassTypeRef :: Nil
624-
if (isCaseClass | isCaseObject)
626+
if (isNonEnumCase || isEnum)
625627
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
626-
if (isEnum)
627-
parents1 = parents1 :+ ref(defn.EnumClass.typeRef)
628628

629629
// derived type classes of non-module classes go to their companions
630630
val (clsDerived, companionDerived) =
@@ -890,6 +890,9 @@ object desugar {
890890
}
891891
}
892892

893+
def enumGetters(getters: EnumGetters)(using Context): Tree =
894+
flatTree(DesugarEnums.enumBaseMeths).withSpan(getters.span)
895+
893896
/** Transform extension construct to list of extension methods */
894897
def extMethods(ext: ExtMethods)(using Context): Tree = flatTree {
895898
for mdef <- ext.methods yield

compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,13 @@ object DesugarEnums {
322322
(ordinal, Nil)
323323
}
324324

325+
def enumBaseMeths(using Context): List[Tree] =
326+
val ordinalDef = DefDef(nme.ordinal, Nil, Nil, ref(defn.IntType), EmptyTree)
327+
val enumLabelDef = DefDef(nme.enumLabel, Nil, Nil, ref(defn.StringClass.typeRef), EmptyTree)
328+
val base = enumLabelDef :: Nil
329+
if isJavaEnum then base
330+
else ordinalDef :: base
331+
325332
def param(name: TermName, typ: Type)(using Context): ValDef = param(name, TypeTree(typ))
326333
def param(name: TermName, tpt: Tree)(using Context): ValDef = ValDef(name, tpt, EmptyTree).withFlags(Param)
327334

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
116116
case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree
117117
case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree
118118
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
119+
case class EnumGetters()(implicit @constructorOnly src: SourceFile) extends Tree
119120

120121
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
121122
// TODO: Make bound a typed tree?
@@ -700,6 +701,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
700701
cpy.Export(tree)(transform(expr), selectors)
701702
case ExtMethods(tparams, vparamss, methods) =>
702703
cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods))
704+
case enums: EnumGetters => enums
703705
case ImportSelector(imported, renamed, bound) =>
704706
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
705707
case Number(_, _) | TypedSplice(_) =>
@@ -761,6 +763,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
761763
this(x, expr)
762764
case ExtMethods(tparams, vparamss, methods) =>
763765
this(vparamss.foldLeft(this(x, tparams))(apply), methods)
766+
case EnumGetters() =>
767+
x
764768
case ImportSelector(imported, renamed, bound) =>
765769
this(this(this(x, imported), renamed), bound)
766770
case Number(_, _) =>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,8 @@ object SymUtils {
168168
self
169169
}
170170

171+
def isScalaEnum(using Context): Boolean = self.is(Enum, butNot=JavaDefined)
172+
171173
/** Does this symbol refer to anonymous classes synthesized by enum desugaring? */
172174
def isEnumAnonymClass(using Context): Boolean =
173175
self.isAnonymousClass && (self.owner.name.eq(nme.DOLLAR_NEW) || self.owner.is(CaseVal))

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
9494
lazy val accessors =
9595
if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased`
9696
else clazz.caseAccessors
97-
val isEnumCase = clazz.derivesFrom(defn.EnumClass) && clazz != defn.EnumClass
97+
val isEnumCase = clazz.classParents.exists(_.typeSymbol.isScalaEnum)
9898
val isEnumValue = isEnumCase && clazz.isAnonymousClass && clazz.classParents.head.classSymbol.is(Enum)
9999
val isNonJavaEnumValue = isEnumValue && !clazz.derivesFrom(defn.JavaEnumClass)
100100

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,20 @@ class Namer { typer: Typer =>
343343
tree.pushAttachment(ExpandedTree, expanded)
344344
}
345345
tree match {
346-
case tree: DefTree => record(desugar.defTree(tree))
347-
case tree: PackageDef => record(desugar.packageDef(tree))
348-
case tree: ExtMethods => record(desugar.extMethods(tree))
349-
case _ =>
346+
case tree: DefTree => record(desugar.defTree(tree))
347+
case tree: PackageDef => record(desugar.packageDef(tree))
348+
case tree: ExtMethods => record(desugar.extMethods(tree))
349+
case tree: EnumGetters => record(desugar.enumGetters(tree))
350+
case _ =>
350351
}
351352
}
352353

353354
/** The expanded version of this tree, or tree itself if not expanded */
354355
def expanded(tree: Tree)(using Context): Tree = tree match {
355-
case _: DefTree | _: PackageDef | _: ExtMethods => tree.attachmentOrElse(ExpandedTree, tree)
356-
case _ => tree
356+
case _: DefTree | _: PackageDef | _: ExtMethods | _: EnumGetters =>
357+
tree.attachmentOrElse(ExpandedTree, tree)
358+
case _ =>
359+
tree
357360
}
358361

359362
/** For all class definitions `stat` in `xstats`: If the companion class is
@@ -925,11 +928,17 @@ class Namer { typer: Typer =>
925928

926929
val TypeDef(name, impl @ Template(constr, _, self, _)) = original
927930

928-
private val (params, rest): (List[Tree], List[Tree]) = impl.body.span {
931+
private val (params, restOfBody): (List[Tree], List[Tree]) = impl.body.span {
929932
case td: TypeDef => td.mods.is(Param)
930933
case vd: ValDef => vd.mods.is(ParamAccessor)
931934
case _ => false
932935
}
936+
private val (restAfterParents, rest): (List[Tree], List[Tree]) =
937+
if original.mods.isEnumClass then
938+
val (imports :: getters :: Nil, stats): @unchecked = restOfBody.splitAt(2)
939+
(getters :: Nil, imports :: stats) // enum getters desugaring needs to test if a parent is java.lang.Enum
940+
else
941+
(Nil, restOfBody)
933942

934943
def init(): Context = index(params)
935944

@@ -1196,6 +1205,7 @@ class Namer { typer: Typer =>
11961205
cls.setNoInitsFlags(parentsKind(parents), untpd.bodyKind(rest))
11971206
if (cls.isNoInitsClass) cls.primaryConstructor.setFlag(StableRealizable)
11981207
processExports(using localCtx)
1208+
index(restAfterParents)(using localCtx)
11991209
}
12001210
}
12011211

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2122,7 +2122,7 @@ class Typer extends Namer
21222122
.withType(dummy.termRef)
21232123
if (!cls.isOneOf(AbstractOrTrait) && !ctx.isAfterTyper)
21242124
checkRealizableBounds(cls, cdef.sourcePos.withSpan(cdef.nameSpan))
2125-
if cls.derivesFrom(defn.EnumClass) then
2125+
if cls.isScalaEnum || firstParent.isScalaEnum then
21262126
checkEnum(cdef, cls, firstParent)
21272127
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)
21282128

@@ -2635,6 +2635,9 @@ class Typer extends Namer
26352635
case (stat: untpd.ExtMethods) :: rest =>
26362636
val xtree = stat.removeAttachment(ExpandedTree).get
26372637
traverse(xtree :: rest)
2638+
case (stat: untpd.EnumGetters) :: rest =>
2639+
val xtree = stat.removeAttachment(ExpandedTree).get
2640+
traverse(xtree :: rest)
26382641
case stat :: rest =>
26392642
val stat1 = typed(stat)(using ctx.exprContext(stat, exprOwner))
26402643
checkStatementPurity(stat1)(stat, exprOwner)

library/src-bootstrapped/scala/Enum.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package scala
22

3-
/** A base trait of all enum classes */
3+
/** A Product that also describes a label and ordinal */
4+
@deprecated("scala.Enum is no longer supported", "3.0.0-M1")
45
trait Enum extends Product, Serializable:
56

67
/** A string uniquely identifying a case of an enum */

library/src-non-bootstrapped/scala/Enum.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,8 @@ package scala
33
/** A base trait of all enum classes */
44
trait Enum extends Product, Serializable:
55

6+
/** A string uniquely identifying a case of an enum */
7+
def enumLabel: String
8+
69
/** A number uniquely identifying a case of an enum */
710
def ordinal: Int

library/src-non-bootstrapped/scala/runtime/EnumValueSerializationProxy.java

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

library/src-non-bootstrapped/scala/runtime/EnumValues.scala

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

tests/run/generic/Enum.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ object runtime {
2222
}
2323
def values: Iterable[E] = myMap.values
2424
}
25-
}
25+
}

0 commit comments

Comments
 (0)