Skip to content

move implementation of ordinal in enums to posttyper #13952

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 1 commit into from
Nov 15, 2021
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
11 changes: 3 additions & 8 deletions compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,12 @@ object DesugarEnums {
* }
*/
private def enumValueCreator(using Context) = {
val fieldMethods = if isJavaEnum then Nil else ordinalMeth(Ident(nme.ordinalDollar_)) :: Nil
val creator = New(Template(
constr = emptyConstructor,
parents = enumClassRef :: scalaRuntimeDot(tpnme.EnumValue) :: Nil,
derived = Nil,
self = EmptyValDef,
body = fieldMethods
body = Nil
).withAttachment(ExtendsSingletonMirror, ()))
DefDef(nme.DOLLAR_NEW,
List(List(param(nme.ordinalDollar_, defn.IntType), param(nme.nameDollar, defn.StringType))),
Expand Down Expand Up @@ -270,8 +269,6 @@ object DesugarEnums {
def param(name: TermName, typ: Type)(using Context): ValDef = param(name, TypeTree(typ))
def param(name: TermName, tpt: Tree)(using Context): ValDef = ValDef(name, tpt, EmptyTree).withFlags(Param)

private def isJavaEnum(using Context): Boolean = enumClass.derivesFrom(defn.JavaEnumClass)

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

Expand All @@ -290,10 +287,8 @@ object DesugarEnums {
expandSimpleEnumCase(name, mods, definesLookups, span)
else {
val (tag, scaffolding) = nextOrdinal(name, CaseKind.Object, definesLookups)
val impl1 = cpy.Template(impl)(
parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue),
body = if isJavaEnum then Nil else ordinalMethLit(tag) :: Nil
).withAttachment(ExtendsSingletonMirror, ())
val impl1 = cpy.Template(impl)(parents = impl.parents :+ scalaRuntimeDot(tpnme.EnumValue), body = Nil)
.withAttachment(ExtendsSingletonMirror, ())
val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods.withAddedFlags(EnumValue, span))
flatTree(vdef :: scaffolding).withSpan(span)
}
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,7 @@ class Definitions {
@tu lazy val NoneModule: Symbol = requiredModule("scala.None")

@tu lazy val EnumClass: ClassSymbol = requiredClass("scala.reflect.Enum")
@tu lazy val Enum_ordinal: Symbol = EnumClass.requiredMethod(nme.ordinal)

@tu lazy val EnumValueSerializationProxyClass: ClassSymbol = requiredClass("scala.runtime.EnumValueSerializationProxy")
@tu lazy val EnumValueSerializationProxyConstructor: TermSymbol =
Expand Down
14 changes: 13 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
myCaseSymbols = defn.caseClassSynthesized
myCaseModuleSymbols = myCaseSymbols.filter(_ ne defn.Any_equals)
myEnumValueSymbols = List(defn.Product_productPrefix)
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString
myNonJavaEnumValueSymbols = myEnumValueSymbols :+ defn.Any_toString :+ defn.Enum_ordinal
}

def valueSymbols(using Context): List[Symbol] = { initSymbols; myValueSymbols }
Expand Down Expand Up @@ -132,6 +132,17 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
else // assume owner is `val Foo = new MyEnum { def ordinal = 0 }`
Literal(Constant(clazz.owner.name.toString))

def ordinalRef: Tree =
if isSimpleEnumValue then // owner is `def $new(_$ordinal: Int, $name: String) = new MyEnum { ... }`
ref(clazz.owner.paramSymss.head.find(_.name == nme.ordinalDollar_).get)
else // val CaseN = new MyEnum { ... def ordinal: Int = n }
val vdef = clazz.owner
val parentEnum = vdef.owner.companionClass
val children = parentEnum.children.zipWithIndex
val candidate: Option[Int] = children.collectFirst { case (child, idx) if child == vdef => idx }
assert(candidate.isDefined, i"could not find child for $vdef")
Literal(Constant(candidate.get))

def toStringBody(vrefss: List[List[Tree]]): Tree =
if (clazz.is(ModuleClass)) ownName
else if (isNonJavaEnumValue) identifierRef
Expand All @@ -143,6 +154,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
case nme.toString_ => toStringBody(vrefss)
case nme.equals_ => equalsBody(vrefss.head.head)
case nme.canEqual_ => canEqualBody(vrefss.head.head)
case nme.ordinal => ordinalRef
case nme.productArity => Literal(Constant(accessors.length))
case nme.productPrefix if isEnumValue => nameRef
case nme.productPrefix => ownName
Expand Down
13 changes: 13 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1217,8 +1217,19 @@ trait Checking {
/** 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.
* 4. Check that user does not implement an `ordinal` method in the body of an enum class.
*/
def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(using Context): Unit = {
def existingDef(sym: Symbol, clazz: ClassSymbol)(using Context): Symbol = // adapted from SyntheticMembers
val existing = sym.matchingMember(clazz.thisType)
if existing != sym && !existing.is(Deferred) then existing else NoSymbol
def checkExistingOrdinal(using Context) =
val decl = existingDef(defn.Enum_ordinal, cls.asClass)
if decl.exists then
if decl.owner == cls then
report.error(em"the ordinal method of enum $cls can not be defined by the user", decl.srcPos)
else
report.error(em"enum $cls can not inherit the concrete ordinal method of ${decl.owner}", cdef.srcPos)
def isEnumAnonCls =
cls.isAnonymousClass
&& cls.owner.isTerm
Expand All @@ -1238,6 +1249,8 @@ trait Checking {
// this test allows inheriting from `Enum` by hand;
// see enum-List-control.scala.
report.error(ClassCannotExtendEnum(cls, firstParent), cdef.srcPos)
if cls.isEnumClass && !isJavaEnum then
checkExistingOrdinal
}

/** Check that the firstParent for an enum case derives from the declaring enum class, if not, adds it as a parent
Expand Down
8 changes: 4 additions & 4 deletions tests/neg/enumsLabel-singleimpl.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
enum Ordinalled {

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

def ordinal: Int = -1
def ordinal: Int = -1 // error: the ordinal method of enum class Ordinalled can not be defined by the user

}

trait HasOrdinal { def ordinal: Int = 23 }

enum MyEnum extends HasOrdinal {
case Foo // error: method ordinal of type => Int needs `override` modifier
enum MyEnum extends HasOrdinal { // error: enum class MyEnum can not inherit the concrete ordinal method of trait HasOrdinal
case Foo
}
6 changes: 6 additions & 0 deletions tests/pos/i13554.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
object StatusCode:
class Matcher

enum StatusCode(m: StatusCode.Matcher):
case InternalServerError extends StatusCode(???)

15 changes: 15 additions & 0 deletions tests/pos/i13554a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
object StatusCode:
enum Matcher:
case ServerError extends Matcher
end Matcher
end StatusCode

enum StatusCode(code: Int, m: StatusCode.Matcher):
case InternalServerError extends StatusCode(500, StatusCode.Matcher.ServerError)
end StatusCode

object Main {
def main(args: Array[String]): Unit = {
println(StatusCode.InternalServerError)
}
}