Skip to content

Commit 0525ba0

Browse files
authored
Merge pull request #9987 from dotty-staging/topic/enum-fromOrdinal
make fromOrdinal public and always available
2 parents 63771ac + 6d50fef commit 0525ba0

File tree

6 files changed

+127
-38
lines changed

6 files changed

+127
-38
lines changed

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

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,29 @@ 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.WILDCARD), 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+
if !enumClass.exists then
175+
// in the case of a double definition of an enum that only defines class cases (see tests/neg/i4470c.scala)
176+
// it seems `enumClass` might be `NoSymbol`; in this case we provide no scaffolding.
177+
Nil
178+
else
179+
scaffolding ::: valueCtor ::: fromOrdinal :: Nil
173180
end enumLookupMethods
174181

175182
/** A creation method for a value of enum type `E`, which is defined as follows:
@@ -278,6 +285,10 @@ object DesugarEnums {
278285
def ordinalMethLit(ord: Int)(using Context): DefDef =
279286
ordinalMeth(Literal(Constant(ord)))
280287

288+
def fromOrdinalMeth(body: Tree => Tree)(using Context): DefDef =
289+
DefDef(nme.fromOrdinal, Nil, (param(nme.ordinal, defn.IntType) :: Nil) :: Nil,
290+
rawRef(enumClass.typeRef), body(Ident(nme.ordinal))).withFlags(Synthetic)
291+
281292
/** Expand a module definition representing a parameterless enum case */
282293
def expandEnumModule(name: TermName, impl: Template, mods: Modifiers, definesLookups: Boolean, span: Span)(using Context): Tree = {
283294
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/neg/i4470c.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
object DuplicatedEnum {
2+
23
enum Maybe[+T] { // error
34
case Some[T](x: T) extends Maybe[T]
45
}
56

67
enum Maybe[+T] { // error
78
case Some[T](x: T) extends Maybe[T]
89
}
10+
11+
enum Color { // error
12+
case Red, Green, Blue
13+
}
14+
15+
enum Color { // error
16+
case Red, Green, Blue
17+
}
18+
19+
enum Tag[T] { // error
20+
case Int extends Tag[Int]
21+
case OfClass[T]()(using val tag: reflect.ClassTag[T]) extends Tag[T] // mix order of class and value
22+
case String extends Tag[String]
23+
}
24+
25+
enum Tag[T] { // error
26+
case Int extends Tag[Int]
27+
case OfClass[T]()(using val tag: reflect.ClassTag[T]) extends Tag[T] // mix order of class and value
28+
case String extends Tag[String]
29+
}
30+
931
}

tests/run/enum-values.scala

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

7+
enum Suits extends java.lang.Enum[Suits]:
8+
case Clubs, Spades, Diamonds, Hearts
9+
410
enum Tag[T]:
511
case Int extends Tag[Int]
12+
case OfClass[T]()(using val tag: reflect.ClassTag[T]) extends Tag[T] // mix order of class and value
613
case String extends Tag[String]
7-
case OfClass[T]()(using val tag: reflect.ClassTag[T]) extends Tag[T]
814

915
enum Expr[-T >: Null]:
1016
case EmptyTree extends Expr[Null]
@@ -16,18 +22,64 @@ enum ListLike[+T]:
1622

1723
enum TypeCtorsK[F[_]]:
1824
case List extends TypeCtorsK[List]
25+
case Const[T]() extends TypeCtorsK[[U] =>> T] // mix order of class and value
1926
case Option extends TypeCtorsK[Option]
20-
case Const[T]() extends TypeCtorsK[[U] =>> T]
2127

2228
enum MixedParams[F[_], G[X,Y] <: collection.Map[X,Y], T]:
2329
case Foo extends MixedParams[List, collection.mutable.LinkedHashMap, Unit]
2430

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

2945
extension [A](t: A) def show = runtime.ScalaRunTime.stringOf(t)
3046

47+
def fetchFromOrdinal[T <: AnyRef & reflect.Enum](companion: FromOrdinal[T], compare: T*): Unit =
48+
for c <- compare do
49+
assert(companion.fromOrdinal(c.ordinal) eq c,
50+
s"$c does not `eq` companion.fromOrdinal(${c.ordinal}), got ${companion.fromOrdinal(c.ordinal)}")
51+
52+
def notFromOrdinal[T <: AnyRef & reflect.Enum](companion: FromOrdinal[T], compare: T): Unit =
53+
cantFind(companion, compare.ordinal)
54+
55+
def cantFind[T](companion: FromOrdinal[T], ordinal: Int): Unit =
56+
try
57+
companion.fromOrdinal(ordinal)
58+
assertFail(s"$companion.fromOrdinal(${ordinal}) did not fail")
59+
catch
60+
case e: java.lang.reflect.InvocationTargetException => // TODO: maybe reflect.Selectable should catch this?
61+
assert(e.getCause.isInstanceOf[java.util.NoSuchElementException]
62+
&& e.getCause.getMessage == ordinal.toString)
63+
64+
fetchFromOrdinal(companion = Color, compare = Red, Green, Blue)
65+
fetchFromOrdinal(companion = Suits, compare = Clubs, Spades, Diamonds, Hearts)
66+
fetchFromOrdinal(companion = Tag, compare = Int, String)
67+
fetchFromOrdinal(companion = Expr, compare = EmptyTree, AnyTree)
68+
fetchFromOrdinal(companion = ListLike, compare = EmptyListLike)
69+
fetchFromOrdinal(companion = TypeCtorsK, compare = List, Option)
70+
fetchFromOrdinal(companion = MixedParams, compare = Foo)
71+
72+
notFromOrdinal(companion = Tag, compare = OfClass[String]())
73+
notFromOrdinal(companion = TypeCtorsK, compare = Const[String]())
74+
notFromOrdinal(companion = ClassOnly, compare = BranchProd(1)) // ClassOnly has the `fromOrdinal` method
75+
76+
cantFind(companion = Color, ordinal = 500) // test default case for enumeration
77+
cantFind(companion = Suits, ordinal = 500) // test default case for Java style enumeration
78+
cantFind(companion = Tag, ordinal = 500) // test default case for mixed adt with non-simple values
79+
cantFind(companion = ClassOnly, ordinal = 500) // should always throw
80+
81+
assert(summon[Mirror.SumOf[ClassOnly]].ordinal(BranchProd(1)) == 0)
82+
3183
val colors: Array[Color] = Color.values
3284
val tags: Array[Tag[?]] = Tag.values
3385
val exprs: Array[Expr[? >: Null]] = Expr.values
@@ -46,7 +98,7 @@ enum MixedParams[F[_], G[X,Y] <: collection.Map[X,Y], T]:
4698
sameAs(typeCtorsK, List, Option)
4799
sameAs(mixedParams, Foo)
48100

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

tests/semanticdb/metac.expect

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ Schema => SemanticDB v4
641641
Uri => Enums.scala
642642
Text => empty
643643
Language => Scala
644-
Symbols => 181 entries
644+
Symbols => 185 entries
645645
Occurrences => 203 entries
646646

647647
Symbols:
@@ -651,37 +651,35 @@ _empty_/Enums.Coin#`<init>`(). => primary ctor <init>
651651
_empty_/Enums.Coin#`<init>`().(value) => param value
652652
_empty_/Enums.Coin#value. => val method value
653653
_empty_/Enums.Coin. => final object Coin
654-
_empty_/Enums.Coin.$fromOrdinal(). => method $fromOrdinal
655-
_empty_/Enums.Coin.$fromOrdinal().(_$ordinal) => param _$ordinal
656654
_empty_/Enums.Coin.$values. => val method $values
657655
_empty_/Enums.Coin.Dime. => case val static enum method Dime
658656
_empty_/Enums.Coin.Dollar. => case val static enum method Dollar
659657
_empty_/Enums.Coin.Nickel. => case val static enum method Nickel
660658
_empty_/Enums.Coin.Penny. => case val static enum method Penny
661659
_empty_/Enums.Coin.Quarter. => case val static enum method Quarter
660+
_empty_/Enums.Coin.fromOrdinal(). => method fromOrdinal
661+
_empty_/Enums.Coin.fromOrdinal().(ordinal) => param ordinal
662662
_empty_/Enums.Coin.valueOf(). => method valueOf
663663
_empty_/Enums.Coin.valueOf().($name) => param $name
664664
_empty_/Enums.Coin.values(). => method values
665665
_empty_/Enums.Colour# => abstract sealed enum class Colour
666666
_empty_/Enums.Colour#`<init>`(). => primary ctor <init>
667667
_empty_/Enums.Colour. => final object Colour
668-
_empty_/Enums.Colour.$fromOrdinal(). => method $fromOrdinal
669-
_empty_/Enums.Colour.$fromOrdinal().(_$ordinal) => param _$ordinal
670668
_empty_/Enums.Colour.$new(). => method $new
671669
_empty_/Enums.Colour.$new().($name) => param $name
672670
_empty_/Enums.Colour.$new().(_$ordinal) => param _$ordinal
673671
_empty_/Enums.Colour.$values. => val method $values
674672
_empty_/Enums.Colour.Blue. => case val static enum method Blue
675673
_empty_/Enums.Colour.Green. => case val static enum method Green
676674
_empty_/Enums.Colour.Red. => case val static enum method Red
675+
_empty_/Enums.Colour.fromOrdinal(). => method fromOrdinal
676+
_empty_/Enums.Colour.fromOrdinal().(ordinal) => param ordinal
677677
_empty_/Enums.Colour.valueOf(). => method valueOf
678678
_empty_/Enums.Colour.valueOf().($name) => param $name
679679
_empty_/Enums.Colour.values(). => method values
680680
_empty_/Enums.Directions# => abstract sealed enum class Directions
681681
_empty_/Enums.Directions#`<init>`(). => primary ctor <init>
682682
_empty_/Enums.Directions. => final object Directions
683-
_empty_/Enums.Directions.$fromOrdinal(). => method $fromOrdinal
684-
_empty_/Enums.Directions.$fromOrdinal().(_$ordinal) => param _$ordinal
685683
_empty_/Enums.Directions.$new(). => method $new
686684
_empty_/Enums.Directions.$new().($name) => param $name
687685
_empty_/Enums.Directions.$new().(_$ordinal) => param _$ordinal
@@ -690,15 +688,15 @@ _empty_/Enums.Directions.East. => case val static enum method East
690688
_empty_/Enums.Directions.North. => case val static enum method North
691689
_empty_/Enums.Directions.South. => case val static enum method South
692690
_empty_/Enums.Directions.West. => case val static enum method West
691+
_empty_/Enums.Directions.fromOrdinal(). => method fromOrdinal
692+
_empty_/Enums.Directions.fromOrdinal().(ordinal) => param ordinal
693693
_empty_/Enums.Directions.valueOf(). => method valueOf
694694
_empty_/Enums.Directions.valueOf().($name) => param $name
695695
_empty_/Enums.Directions.values(). => method values
696696
_empty_/Enums.Maybe# => abstract sealed enum class Maybe
697697
_empty_/Enums.Maybe#[A] => covariant typeparam A
698698
_empty_/Enums.Maybe#`<init>`(). => primary ctor <init>
699699
_empty_/Enums.Maybe. => final object Maybe
700-
_empty_/Enums.Maybe.$fromOrdinal(). => method $fromOrdinal
701-
_empty_/Enums.Maybe.$fromOrdinal().(_$ordinal) => param _$ordinal
702700
_empty_/Enums.Maybe.$values. => val method $values
703701
_empty_/Enums.Maybe.Just# => final case enum class Just
704702
_empty_/Enums.Maybe.Just#[A] => covariant typeparam A
@@ -721,6 +719,8 @@ _empty_/Enums.Maybe.Just.unapply(). => method unapply
721719
_empty_/Enums.Maybe.Just.unapply().(x$1) => param x$1
722720
_empty_/Enums.Maybe.Just.unapply().[A] => typeparam A
723721
_empty_/Enums.Maybe.None. => case val static enum method None
722+
_empty_/Enums.Maybe.fromOrdinal(). => method fromOrdinal
723+
_empty_/Enums.Maybe.fromOrdinal().(ordinal) => param ordinal
724724
_empty_/Enums.Maybe.valueOf(). => method valueOf
725725
_empty_/Enums.Maybe.valueOf().($name) => param $name
726726
_empty_/Enums.Maybe.values(). => method values
@@ -744,14 +744,14 @@ _empty_/Enums.Planet.Neptune. => case val static enum method Neptune
744744
_empty_/Enums.Planet.Saturn. => case val static enum method Saturn
745745
_empty_/Enums.Planet.Uranus. => case val static enum method Uranus
746746
_empty_/Enums.Planet.Venus. => case val static enum method Venus
747+
_empty_/Enums.Planet.fromOrdinal(). => method fromOrdinal
748+
_empty_/Enums.Planet.fromOrdinal().(ordinal) => param ordinal
747749
_empty_/Enums.Planet.valueOf(). => method valueOf
748750
_empty_/Enums.Planet.valueOf().($name) => param $name
749751
_empty_/Enums.Planet.values(). => method values
750752
_empty_/Enums.Suits# => abstract sealed enum class Suits
751753
_empty_/Enums.Suits#`<init>`(). => primary ctor <init>
752754
_empty_/Enums.Suits. => final object Suits
753-
_empty_/Enums.Suits.$fromOrdinal(). => method $fromOrdinal
754-
_empty_/Enums.Suits.$fromOrdinal().(_$ordinal) => param _$ordinal
755755
_empty_/Enums.Suits.$new(). => method $new
756756
_empty_/Enums.Suits.$new().($name) => param $name
757757
_empty_/Enums.Suits.$new().(_$ordinal) => param _$ordinal
@@ -765,26 +765,26 @@ _empty_/Enums.Suits.extension_isBlack(). => method extension_isBlack
765765
_empty_/Enums.Suits.extension_isBlack().(suit) => param suit
766766
_empty_/Enums.Suits.extension_isRed(). => method extension_isRed
767767
_empty_/Enums.Suits.extension_isRed().(suit) => param suit
768+
_empty_/Enums.Suits.fromOrdinal(). => method fromOrdinal
769+
_empty_/Enums.Suits.fromOrdinal().(ordinal) => param ordinal
768770
_empty_/Enums.Suits.valueOf(). => method valueOf
769771
_empty_/Enums.Suits.valueOf().($name) => param $name
770772
_empty_/Enums.Suits.values(). => method values
771773
_empty_/Enums.Tag# => abstract sealed enum class Tag
772774
_empty_/Enums.Tag#[A] => typeparam A
773775
_empty_/Enums.Tag#`<init>`(). => primary ctor <init>
774776
_empty_/Enums.Tag. => final object Tag
775-
_empty_/Enums.Tag.$fromOrdinal(). => method $fromOrdinal
776-
_empty_/Enums.Tag.$fromOrdinal().(_$ordinal) => param _$ordinal
777777
_empty_/Enums.Tag.$values. => val method $values
778778
_empty_/Enums.Tag.BooleanTag. => case val static enum method BooleanTag
779779
_empty_/Enums.Tag.IntTag. => case val static enum method IntTag
780+
_empty_/Enums.Tag.fromOrdinal(). => method fromOrdinal
781+
_empty_/Enums.Tag.fromOrdinal().(ordinal) => param ordinal
780782
_empty_/Enums.Tag.valueOf(). => method valueOf
781783
_empty_/Enums.Tag.valueOf().($name) => param $name
782784
_empty_/Enums.Tag.values(). => method values
783785
_empty_/Enums.WeekDays# => abstract sealed enum class WeekDays
784786
_empty_/Enums.WeekDays#`<init>`(). => primary ctor <init>
785787
_empty_/Enums.WeekDays. => final object WeekDays
786-
_empty_/Enums.WeekDays.$fromOrdinal(). => method $fromOrdinal
787-
_empty_/Enums.WeekDays.$fromOrdinal().(_$ordinal) => param _$ordinal
788788
_empty_/Enums.WeekDays.$new(). => method $new
789789
_empty_/Enums.WeekDays.$new().($name) => param $name
790790
_empty_/Enums.WeekDays.$new().(_$ordinal) => param _$ordinal
@@ -796,6 +796,8 @@ _empty_/Enums.WeekDays.Sunday. => case val static enum method Sunday
796796
_empty_/Enums.WeekDays.Thursday. => case val static enum method Thursday
797797
_empty_/Enums.WeekDays.Tuesday. => case val static enum method Tuesday
798798
_empty_/Enums.WeekDays.Wednesday. => case val static enum method Wednesday
799+
_empty_/Enums.WeekDays.fromOrdinal(). => method fromOrdinal
800+
_empty_/Enums.WeekDays.fromOrdinal().(ordinal) => param ordinal
799801
_empty_/Enums.WeekDays.valueOf(). => method valueOf
800802
_empty_/Enums.WeekDays.valueOf().($name) => param $name
801803
_empty_/Enums.WeekDays.values(). => method values
@@ -817,6 +819,8 @@ _empty_/Enums.`<:<`.Refl.toString(). => method toString
817819
_empty_/Enums.`<:<`.Refl.unapply(). => method unapply
818820
_empty_/Enums.`<:<`.Refl.unapply().(x$1) => param x$1
819821
_empty_/Enums.`<:<`.Refl.unapply().[C] => typeparam C
822+
_empty_/Enums.`<:<`.fromOrdinal(). => method fromOrdinal
823+
_empty_/Enums.`<:<`.fromOrdinal().(ordinal) => param ordinal
820824
_empty_/Enums.`<:<`.given_T(). => final implicit method given_T
821825
_empty_/Enums.`<:<`.given_T().[T] => typeparam T
822826
_empty_/Enums.extension_unwrap(). => method extension_unwrap

0 commit comments

Comments
 (0)