Skip to content

fix #9873: move scala.Enum to scala.reflect.Enum #9942

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ object desugar {
val isCaseObject = mods.is(Case) && isObject
val isEnum = mods.isEnumClass && !mods.is(Module)
def isEnumCase = mods.isEnumCase
def isNonEnumCase = !isEnumCase && (isCaseClass || isCaseObject)
val isValueClass = parents.nonEmpty && isAnyVal(parents.head)
// This is not watertight, but `extends AnyVal` will be replaced by `inline` later.

Expand Down Expand Up @@ -621,10 +622,10 @@ object desugar {
var parents1 = parents
if (isEnumCase && parents.isEmpty)
parents1 = enumClassTypeRef :: Nil
if (isCaseClass | isCaseObject)
if (isNonEnumCase)
parents1 = parents1 :+ scalaDot(str.Product.toTypeName) :+ scalaDot(nme.Serializable.toTypeName)
if (isEnum)
parents1 = parents1 :+ ref(defn.EnumClass.typeRef)
parents1 = parents1 :+ ref(defn.EnumClass)

// derived type classes of non-module classes go to their companions
val (clsDerived, companionDerived) =
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,10 @@ object DesugarEnums {
private def isJavaEnum(using Context): Boolean = enumClass.derivesFrom(defn.JavaEnumClass)

def ordinalMeth(body: Tree)(using Context): DefDef =
DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body)
DefDef(nme.ordinal, Nil, Nil, TypeTree(defn.IntType), body).withAddedFlags(Synthetic)

def enumLabelMeth(body: Tree)(using Context): DefDef =
DefDef(nme.enumLabel, Nil, Nil, TypeTree(defn.StringType), body)
DefDef(nme.enumLabel, Nil, Nil, TypeTree(defn.StringType), body).withAddedFlags(Synthetic)

def ordinalMethLit(ord: Int)(using Context): DefDef =
ordinalMeth(Literal(Constant(ord)))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ class Definitions {
@tu lazy val SomeClass: ClassSymbol = requiredClass("scala.Some")
@tu lazy val NoneModule: Symbol = requiredModule("scala.None")

@tu lazy val EnumClass: ClassSymbol = requiredClass("scala.Enum")
@tu lazy val EnumClass: ClassSymbol = requiredClass("scala.reflect.Enum")

@tu lazy val EnumValueSerializationProxyClass: ClassSymbol = requiredClass("scala.runtime.EnumValueSerializationProxy")
@tu lazy val EnumValueSerializationProxyConstructor: TermSymbol =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import scala.jdk.CollectionConverters._
import collection.mutable
import java.nio.file.Paths

import dotty.tools.dotc.transform.SymUtils._

import PartialFunction.condOpt

import ast.untpd.{given _}
Expand Down Expand Up @@ -191,7 +193,7 @@ class ExtractSemanticDB extends Phase:
val selfSpan = tree.self.span
if selfSpan.exists && selfSpan.hasLength then
traverse(tree.self)
if tree.symbol.owner.is(Enum, butNot=Case) then
if tree.symbol.owner.isEnumClass then
tree.body.foreachUntilImport(traverse).foreach(traverse) // the first import statement
else
tree.body.foreach(traverse)
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/SymUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,9 @@ object SymUtils {
self
}

def isEnum(using Context): Boolean = self.is(Enum, butNot = JavaDefined)
def isEnumClass(using Context): Boolean = isEnum && !self.is(Case)

/** Does this symbol refer to anonymous classes synthesized by enum desugaring? */
def isEnumAnonymClass(using Context): Boolean =
self.isAnonymousClass && (self.owner.name.eq(nme.DOLLAR_NEW) || self.owner.is(CaseVal))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
lazy val accessors =
if (isDerivedValueClass(clazz)) clazz.paramAccessors.take(1) // Tail parameters can only be `erased`
else clazz.caseAccessors
val isEnumCase = clazz.derivesFrom(defn.EnumClass) && clazz != defn.EnumClass
val isEnumValue = isEnumCase && clazz.isAnonymousClass && clazz.classParents.head.classSymbol.is(Enum)
val isEnumValue = clazz.isAnonymousClass && clazz.classParents.head.classSymbol.is(Enum)
val isNonJavaEnumValue = isEnumValue && !clazz.derivesFrom(defn.JavaEnumClass)

val symbolsToSynthesize: List[Symbol] =
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ trait Checking {
if !Inliner.inInlineMethod && !ctx.isInlineContext then
report.error(em"$what can only be used in an inline method", pos)

/** 1. Check that all case classes that extend `scala.Enum` are `enum` cases
/** 1. Check that all case classes that extend `scala.reflect.Enum` are `enum` cases
* 2. Check that parameterised `enum` cases do not extend java.lang.Enum.
* 3. Check that only a static `enum` base class can extend java.lang.Enum.
*/
Expand All @@ -1094,7 +1094,7 @@ trait Checking {
// 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;
// this test allows inheriting from `Enum` by hand;
// see enum-List-control.scala.
report.error(ClassCannotExtendEnum(cls, firstParent), cdef.srcPos)
}
Expand Down
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2106,7 +2106,8 @@ class Typer extends Namer
val constr1 = typed(constr).asInstanceOf[DefDef]
val parentsWithClass = ensureFirstTreeIsClass(parents.mapconserve(typedParent).filterConserve(!_.isEmpty), cdef.nameSpan)
val parents1 = ensureConstrCall(cls, parentsWithClass)(using superCtx)
val firstParent = parents1.head.tpe.dealias.typeSymbol
val firstParentTpe = parents1.head.tpe.dealias
val firstParent = firstParentTpe.typeSymbol

checkEnumParent(cls, firstParent)

Expand All @@ -2123,7 +2124,7 @@ class Typer extends Namer
.withType(dummy.termRef)
if (!cls.isOneOf(AbstractOrTrait) && !ctx.isAfterTyper)
checkRealizableBounds(cls, cdef.sourcePos.withSpan(cdef.nameSpan))
if cls.derivesFrom(defn.EnumClass) then
if cls.isEnum || firstParentTpe.classSymbol.isEnum then
checkEnum(cdef, cls, firstParent)
val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1), cls)

Expand Down
8 changes: 4 additions & 4 deletions docs/docs/reference/enums/enums.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,14 @@ For a more in-depth example of using Scala 3 enums from Java, see [this test](ht

### Implementation

Enums are represented as `sealed` classes that extend the `scala.Enum` trait.
Enums are represented as `sealed` classes that extend the `scala.reflect.Enum` trait.
This trait defines two public methods, `ordinal` and `enumLabel`:

```scala
package scala
package scala.reflect

/** A base trait of all enum classes */
trait Enum extends Product with Serializable {
/** A base trait of all Scala enum definitions */
super trait Enum extends Any with Product with Serializable {

/** A string uniquely identifying a case of an enum */
def enumLabel: String
Expand Down
11 changes: 3 additions & 8 deletions library/src-bootstrapped/scala/Enum.scala
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
package scala

/** A base trait of all enum classes */
trait Enum extends Product, Serializable:

/** A string uniquely identifying a case of an enum */
def enumLabel: String

/** A number uniquely identifying a case of an enum */
def ordinal: Int
/** A base trait of all Scala enum definitions */
@deprecated("scala.Enum has moved to scala.reflect.Enum", "3.0.0-M1")
type Enum = scala.reflect.Enum
10 changes: 10 additions & 0 deletions library/src-bootstrapped/scala/reflect/Enum.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package scala.reflect

/** A base trait of all Scala enum definitions */
super trait Enum extends Any, Product, Serializable:
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added Any super class so it will be eliminated by drop super traits when the common superclass is AnyRef


/** A string uniquely identifying a case of an enum */
def enumLabel: String

/** A number uniquely identifying a case of an enum */
def ordinal: Int
3 changes: 3 additions & 0 deletions library/src-non-bootstrapped/scala/Enum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ package scala
/** A base trait of all enum classes */
trait Enum extends Product, Serializable:

/** A string uniquely identifying a case of an enum */
def enumLabel: String

/** A number uniquely identifying a case of an enum */
def ordinal: Int

This file was deleted.

21 changes: 0 additions & 21 deletions library/src-non-bootstrapped/scala/runtime/EnumValues.scala

This file was deleted.

1 change: 0 additions & 1 deletion tastydoc/test/dotty/tastydoc/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ class Tests {
//Individual files
"scala.\\$times\\$colon*",
"scala.Conversion*",
"scala.Enum*",
"scala.Eql*",
"scala.forceInline*",
"scala.FunctionXXL*",
Expand Down
15 changes: 15 additions & 0 deletions tests/neg/enumsLabel-overrides.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Mixin { def enumLabel: String = "mixin" }

enum Mixed extends Mixin {
case B // error: overriding method enumLabel in trait Mixin of type => String;
}

enum MixedAlso {
case C extends MixedAlso with Mixin // error: overriding method enumLabel in trait Mixin of type => String;
}

trait HasEnumLabel { def enumLabel: String }

enum MyEnum extends HasEnumLabel {
case D // ok
}
15 changes: 15 additions & 0 deletions tests/neg/enumsLabel-singleimpl.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
enum Labelled {

case A // error: method enumLabel of type => String needs `override` modifier

def enumLabel: String = "nolabel"

}

enum Ordinalled {

case A // error: method ordinal of type => Int needs `override` modifier

def ordinal: Int = -1

}
22 changes: 0 additions & 22 deletions tests/neg/enumsLabelDef.scala

This file was deleted.

11 changes: 11 additions & 0 deletions tests/neg/supertraits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ case object b
val y = if ??? then a else b
val y1: Product = y // error
val y2: Serializable = y // error

enum Color {
case Red, Green, Blue
}

enum Nucleobase {
case A, C, G, T
}

val z = if ??? then Color.Red else Nucleobase.G
val z1: reflect.Enum = z // error: Found: (z : Object) Required: reflect.Enum
6 changes: 4 additions & 2 deletions tests/patmat/i7186.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import MIPS._

import deriving.Mirror.SumOf

object MIPS {
type Labels = Label | ControlLabel
type Src = Register | Constant
Expand Down Expand Up @@ -233,8 +235,8 @@ object printMips {
def getScopedLabel(s: Scoped): String =
"L" + getScopedId(s)

def printEnum[E](e: String => Enum, t: E, code: String) = {
val num = e(t.toString).ordinal
def printEnum[E: SumOf](e: String => E, t: E, code: String) = {
val num = summon[SumOf[E]].ordinal(e(t.toString))
s"$code$num"
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/enum-List-control.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
abstract sealed class List[T] extends Enum
abstract sealed class List[T] extends reflect.Enum
object List {
final class Cons[T](x: T, xs: List[T]) extends List[T] {
def ordinal = 0
Expand Down
2 changes: 1 addition & 1 deletion tests/run/generic/Enum.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ object runtime {
}
def values: Iterable[E] = myMap.values
}
}
}