Skip to content

Commit 77a53cf

Browse files
committed
add enumLabel to Mirror.Sum
1 parent 331cfcb commit 77a53cf

File tree

4 files changed

+133
-0
lines changed

4 files changed

+133
-0
lines changed

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,34 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
513513
Match(param, cases)
514514
}
515515

516+
/** For an enum T:
517+
*
518+
* def enumLabel(x: MirroredMonoType) = x.enumLabel
519+
*
520+
* For sealed trait with children of normalized types C_1, ..., C_n:
521+
*
522+
* def enumLabel(x: MirroredMonoType) = x match {
523+
* case _: C_1 => "C_1"
524+
* ...
525+
* case _: C_n => "C_n"
526+
* }
527+
*
528+
* Here, the normalized type of a class C is C[?, ...., ?] with
529+
* a wildcard for each type parameter. The normalized type of an object
530+
* O is O.type.
531+
*/
532+
def enumLabelBody(cls: Symbol, param: Tree)(using Context): Tree =
533+
if (cls.is(Enum)) param.select(nme.enumLabel).ensureApplied
534+
else {
535+
val cases =
536+
for ((child, idx) <- cls.children.zipWithIndex) yield {
537+
val patType = if (child.isTerm) child.termRef else child.rawTypeRef
538+
val pat = Typed(untpd.Ident(nme.WILDCARD).withType(patType), TypeTree(patType))
539+
CaseDef(pat, EmptyTree, Literal(Constant(child.name.toString)))
540+
}
541+
Match(param, cases)
542+
}
543+
516544
/** - If `impl` is the companion of a generic sum, add `deriving.Mirror.Sum` parent
517545
* and `MirroredMonoType` and `ordinal` members.
518546
* - If `impl` is the companion of a generic product, add `deriving.Mirror.Product` parent
@@ -564,6 +592,8 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
564592
addParent(defn.Mirror_SumClass.typeRef)
565593
addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), cls,
566594
ordinalBody(_, _))
595+
addMethod(nme.enumLabel, MethodType(monoType.typeRef :: Nil, defn.StringType), cls,
596+
enumLabelBody(_, _))
567597
}
568598

569599
if (clazz.is(Module)) {
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package scala
2+
3+
import quoted._
4+
5+
object deriving {
6+
7+
/** Mirrors allows typelevel access to enums, case classes and objects, and their sealed parents.
8+
*/
9+
sealed trait Mirror {
10+
11+
/** The mirrored *-type */
12+
type MirroredMonoType
13+
14+
/** The name of the type */
15+
type MirroredLabel <: String
16+
17+
/** The names of the product elements */
18+
type MirroredElemLabels <: Tuple
19+
}
20+
21+
object Mirror {
22+
23+
/** The Mirror for a sum type */
24+
trait Sum extends Mirror { self =>
25+
/** The ordinal number of the case class of `x`. For enums, `ordinal(x) == x.ordinal` */
26+
def ordinal(x: MirroredMonoType): Int
27+
/** The case label of the case class of `x`. For enums, `enumLabel(x) == x.enumLabel` */
28+
def enumLabel(x: MirroredMonoType): String
29+
}
30+
31+
/** The Mirror for a product type */
32+
trait Product extends Mirror {
33+
34+
/** Create a new instance of type `T` with elements taken from product `p`. */
35+
def fromProduct(p: scala.Product): MirroredMonoType
36+
}
37+
38+
trait Singleton extends Product {
39+
type MirroredMonoType = this.type
40+
type MirroredType = this.type
41+
type MirroredElemTypes = EmptyTuple
42+
type MirroredElemLabels = EmptyTuple
43+
def fromProduct(p: scala.Product) = this
44+
}
45+
46+
/** A proxy for Scala 2 singletons, which do not inherit `Singleton` directly */
47+
class SingletonProxy(val value: AnyRef) extends Product {
48+
type MirroredMonoType = value.type
49+
type MirroredType = value.type
50+
type MirroredElemTypes = EmptyTuple
51+
type MirroredElemLabels = EmptyTuple
52+
def fromProduct(p: scala.Product) = value
53+
}
54+
55+
type Of[T] = Mirror { type MirroredType = T; type MirroredMonoType = T ; type MirroredElemTypes <: Tuple }
56+
type ProductOf[T] = Mirror.Product { type MirroredType = T; type MirroredMonoType = T ; type MirroredElemTypes <: Tuple }
57+
type SumOf[T] = Mirror.Sum { type MirroredType = T; type MirroredMonoType = T; type MirroredElemTypes <: Tuple }
58+
}
59+
60+
/** Helper class to turn arrays into products */
61+
class ArrayProduct(val elems: Array[AnyRef]) extends Product {
62+
def this(size: Int) = this(new Array[AnyRef](size))
63+
def canEqual(that: Any): Boolean = true
64+
def productElement(n: Int) = elems(n)
65+
def productArity = elems.length
66+
override def productIterator: Iterator[Any] = elems.iterator
67+
def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef]
68+
}
69+
70+
/** The empty product */
71+
object EmptyProduct extends ArrayProduct(Array.emptyObjectArray)
72+
73+
/** Helper method to select a product element */
74+
def productElement[T](x: Any, idx: Int) =
75+
x.asInstanceOf[Product].productElement(idx).asInstanceOf[T]
76+
}

tests/run/enum-mirror-sumOf.scala

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import Nat._, Lst._
2+
3+
def enumLabelOf[E](e: E)(using s: deriving.Mirror.SumOf[E]): String = s.enumLabel(e)
4+
def ordinalOf[E](e: E)(using s: deriving.Mirror.SumOf[E]): Int = s.ordinal(e)
5+
6+
enum Nat:
7+
case S(pred: Nat)
8+
case Z
9+
10+
sealed abstract class Lst[+T]
11+
object Lst:
12+
final case class Cdr[+T](t: T, ts: Lst[T]) extends Lst[T]
13+
case object NIL extends Lst[Nothing]
14+
15+
@main def Test =
16+
17+
// labels
18+
assert(enumLabelOf(S(Z)) == "S")
19+
assert(enumLabelOf(Z) == "Z")
20+
assert(enumLabelOf(Cdr(1, NIL): Lst[Int]) == "Cdr")
21+
assert(enumLabelOf(NIL: Lst[Int]) == "NIL")
22+
23+
// ordinals
24+
assert(ordinalOf(S(Z)) == 0)
25+
assert(ordinalOf(Z) == 1)
26+
assert(ordinalOf(Cdr(1, NIL): Lst[Int]) == 0)
27+
assert(ordinalOf(NIL: Lst[Int]) == 1)

0 commit comments

Comments
 (0)