Skip to content

Commit 66885a3

Browse files
committed
make fromOrdinal public and always available
1 parent 0011538 commit 66885a3

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:
@@ -278,6 +280,10 @@ object DesugarEnums {
278280
def ordinalMethLit(ord: Int)(using Context): DefDef =
279281
ordinalMeth(Literal(Constant(ord)))
280282

283+
def fromOrdinalMeth(body: Tree => Tree)(using Context): DefDef =
284+
DefDef(nme.fromOrdinal, Nil, List(param(nme.ordinalDollar_, defn.IntType) :: Nil),
285+
rawRef(enumClass.typeRef), body(Ident(nme.ordinalDollar_))).withFlags(Synthetic)
286+
281287
/** Expand a module definition representing a parameterless enum case */
282288
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, definesLookups: Boolean, span: Span)(using Context): Tree = {
283289
assert(impl.body.isEmpty)

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ object StdNames {
617617
val using: N = "using"
618618
val value: N = "value"
619619
val valueOf : N = "valueOf"
620-
val fromOrdinalDollar: N = "$fromOrdinal"
620+
val fromOrdinal: N = "fromOrdinal"
621621
val values: N = "values"
622622
val view_ : N = "view"
623623
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
@@ -429,7 +429,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
429429
* and not deriving from `java.lang.Enum` add the method:
430430
*
431431
* private def readResolve(): AnyRef =
432-
* MyEnum.$fromOrdinal(this.ordinal)
432+
* MyEnum.fromOrdinal(this.ordinal)
433433
*
434434
* unless an implementation already exists, otherwise do nothing.
435435
*/
@@ -443,7 +443,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
443443
List(
444444
DefDef(readResolveDef(clazz),
445445
_ => ref(clazz.owner.owner.sourceModule)
446-
.select(nme.fromOrdinalDollar)
446+
.select(nme.fromOrdinal)
447447
.appliedTo(This(clazz).select(nme.ordinal).ensureApplied))
448448
.withSpan(ctx.owner.span.focus))
449449
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)