Skip to content

Commit 732bacc

Browse files
committed
make fromOrdinal public and always available
1 parent c900cde commit 732bacc

File tree

4 files changed

+69
-23
lines changed

4 files changed

+69
-23
lines changed

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

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,24 @@ object DesugarEnums {
154154
private def enumLookupMethods(constraints: EnumConstraints)(using Context): List[Tree] =
155155
def scaffolding: List[Tree] = if constraints.cached then enumScaffolding(constraints.enumCases.map(_._2)) else Nil
156156
def valueCtor: List[Tree] = if constraints.requiresCreator then enumValueCreator :: Nil else Nil
157-
def byOrdinal: List[Tree] =
158-
if isJavaEnum || !constraints.cached then Nil
157+
def fromOrdinal: Tree =
158+
def throwArg(ordinal: Tree) =
159+
Throw(New(TypeTree(defn.NoSuchElementExceptionType), List(Select(ordinal, nme.toString_) :: Nil)))
160+
if !constraints.cached then
161+
fromOrdinalMeth(throwArg)
159162
else
160-
val defaultCase =
161-
val ord = Ident(nme.ordinal)
162-
val err = Throw(New(TypeTree(defn.IndexOutOfBoundsException.typeRef), List(Select(ord, nme.toString_) :: Nil)))
163-
CaseDef(ord, EmptyTree, err)
164-
val valueCases = constraints.enumCases.map((i, enumValue) =>
165-
CaseDef(Literal(Constant(i)), EmptyTree, enumValue)
166-
) ::: defaultCase :: Nil
167-
val fromOrdinalDef = DefDef(nme.fromOrdinalDollar, Nil, List(param(nme.ordinalDollar_, defn.IntType) :: Nil),
168-
rawRef(enumClass.typeRef), Match(Ident(nme.ordinalDollar_), valueCases))
169-
.withFlags(Synthetic | Private)
170-
fromOrdinalDef :: Nil
171-
172-
scaffolding ::: valueCtor ::: byOrdinal
163+
def default(ordinal: Tree) =
164+
CaseDef(Ident(nme.x_0), EmptyTree, throwArg(ordinal))
165+
if constraints.isEnumeration then
166+
fromOrdinalMeth(ordinal =>
167+
Try(Apply(valuesDot(nme.apply), ordinal), default(ordinal) :: Nil, EmptyTree))
168+
else
169+
fromOrdinalMeth(ordinal =>
170+
Match(ordinal,
171+
constraints.enumCases.map((i, enumValue) => CaseDef(Literal(Constant(i)), EmptyTree, enumValue))
172+
:+ default(ordinal)))
173+
174+
scaffolding ::: valueCtor ::: fromOrdinal :: Nil
173175
end enumLookupMethods
174176

175177
/** A creation method for a value of enum type `E`, which is defined as follows:
@@ -287,6 +289,10 @@ object DesugarEnums {
287289
def enumLabelMeth(body: Tree)(using Context): DefDef =
288290
DefDef(nme.enumLabel, Nil, Nil, TypeTree(defn.StringType), body).withAddedFlags(Synthetic)
289291

292+
def fromOrdinalMeth(body: Tree => Tree)(using Context): DefDef =
293+
DefDef(nme.fromOrdinal, Nil, List(param(nme.ordinalDollar_, defn.IntType) :: Nil),
294+
rawRef(enumClass.typeRef), body(Ident(nme.ordinalDollar_))).withFlags(Synthetic)
295+
290296
def ordinalMethLit(ord: Int)(using Context): DefDef =
291297
ordinalMeth(Literal(Constant(ord)))
292298

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,7 @@ object StdNames {
618618
val using: N = "using"
619619
val value: N = "value"
620620
val valueOf : N = "valueOf"
621-
val fromOrdinalDollar: N = "$fromOrdinal"
621+
val fromOrdinal: N = "fromOrdinal"
622622
val values: N = "values"
623623
val view_ : N = "view"
624624
val wait_ : N = "wait"

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
418418
* and not deriving from `java.lang.Enum` add the method:
419419
*
420420
* private def readResolve(): AnyRef =
421-
* MyEnum.$fromOrdinal(this.ordinal)
421+
* MyEnum.fromOrdinal(this.ordinal)
422422
*
423423
* unless an implementation already exists, otherwise do nothing.
424424
*/
@@ -432,7 +432,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
432432
List(
433433
DefDef(readResolveDef(clazz),
434434
_ => ref(clazz.owner.owner.sourceModule)
435-
.select(nme.fromOrdinalDollar)
435+
.select(nme.fromOrdinal)
436436
.appliedTo(This(clazz).select(nme.ordinal).ensureApplied))
437437
.withSpan(ctx.owner.span.focus))
438438
else

tests/run/enum-values.scala

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import reflect.Selectable.reflectiveSelectable
2+
import deriving.Mirror
3+
14
enum Color:
25
case Red, Green, Blue
36

47
enum Tag[T]:
58
case Int extends Tag[Int]
9+
case OfClass[T]()(using val tag: reflect.ClassTag[T]) extends Tag[T] // mix order of class and value
610
case String extends Tag[String]
7-
case OfClass[T]()(using val tag: reflect.ClassTag[T]) extends Tag[T]
811

912
enum Expr[-T >: Null]:
1013
case EmptyTree extends Expr[Null]
@@ -16,18 +19,55 @@ enum ListLike[+T]:
1619

1720
enum TypeCtorsK[F[_]]:
1821
case List extends TypeCtorsK[List]
22+
case Const[T]() extends TypeCtorsK[[U] =>> T] // mix order of class and value
1923
case Option extends TypeCtorsK[Option]
20-
case Const[T]() extends TypeCtorsK[[U] =>> T]
2124

2225
enum MixedParams[F[_], G[X,Y] <: collection.Map[X,Y], T]:
2326
case Foo extends MixedParams[List, collection.mutable.LinkedHashMap, Unit]
2427

28+
enum ClassOnly: // this should still generate the `ordinal` and `fromOrdinal` companion methods
29+
case BranchProd(i: Int)
30+
2531
@main def Test: Unit =
26-
import Color._, Tag._, Expr._, ListLike._, TypeCtorsK._, MixedParams._
27-
import reflect.Selectable.reflectiveSelectable
32+
import Color._, Tag._, Expr._, ListLike._, TypeCtorsK._, MixedParams._, ClassOnly._
33+
34+
type FromOrdinal[T <: AnyRef] = {
35+
def fromOrdinal(ordinal: Int): T
36+
}
37+
38+
type ValueOf[T <: AnyRef] = {
39+
def valueOf(s: String): T
40+
}
2841

2942
extension [A](t: A) def show = runtime.ScalaRunTime.stringOf(t)
3043

44+
def fetchFromOrdinal[T <: AnyRef & reflect.Enum](companion: FromOrdinal[T], compare: T*): Unit =
45+
for c <- compare do
46+
assert(companion.fromOrdinal(c.ordinal) eq c,
47+
s"$c does not `eq` companion.fromOrdinal(${c.ordinal}), got ${companion.fromOrdinal(c.ordinal)}")
48+
49+
def notFromOrdinal[T <: AnyRef & reflect.Enum](companion: FromOrdinal[T], compare: T): Unit =
50+
try
51+
companion.fromOrdinal(compare.ordinal)
52+
assertFail(s"$companion.fromOrdinal(${compare.ordinal}) did not fail")
53+
catch
54+
case e: java.lang.reflect.InvocationTargetException => // TODO: maybe reflect.Selectable should catch this?
55+
assert(e.getCause.isInstanceOf[java.util.NoSuchElementException]
56+
&& e.getCause.getMessage == compare.ordinal.toString)
57+
58+
fetchFromOrdinal(companion = Color, compare = Red, Green, Blue)
59+
fetchFromOrdinal(companion = Tag, compare = Int, String)
60+
fetchFromOrdinal(companion = Expr, compare = EmptyTree, AnyTree)
61+
fetchFromOrdinal(companion = ListLike, compare = EmptyListLike)
62+
fetchFromOrdinal(companion = TypeCtorsK, compare = List, Option)
63+
fetchFromOrdinal(companion = MixedParams, compare = Foo)
64+
65+
notFromOrdinal(companion = Tag, compare = OfClass[String]())
66+
notFromOrdinal(companion = TypeCtorsK, compare = Const[String]())
67+
notFromOrdinal(companion = ClassOnly, compare = BranchProd(1)) // ClassOnly has the `fromOrdinal` method
68+
69+
assert(summon[Mirror.SumOf[ClassOnly]].ordinal(BranchProd(1)) == 0)
70+
3171
val colors: Array[Color] = Color.values
3272
val tags: Array[Tag[?]] = Tag.values
3373
val exprs: Array[Expr[? >: Null]] = Expr.values
@@ -46,7 +86,7 @@ enum MixedParams[F[_], G[X,Y] <: collection.Map[X,Y], T]:
4686
sameAs(typeCtorsK, List, Option)
4787
sameAs(mixedParams, Foo)
4888

49-
def singleton[E <: AnyRef](value: E, name: String, companion: { def valueOf(s: String): E}) =
89+
def singleton[E <: AnyRef](value: E, name: String, companion: ValueOf[E]) =
5090
val lookup = companion.valueOf(name)
5191
assert(value eq lookup, s"${value.show} is not identical to ${lookup.show}")
5292

0 commit comments

Comments
 (0)