Skip to content

Commit 6acaf95

Browse files
Add portotype for Expr[Mirror] methods (#18072)
Getting the info from the mirror in a macro is a bit tricky and verbose. The methods in this portotype intend to make this process trivial. ```scala private def myMacroExpr[T: Type](mirror: Expr[Mirror.Of[T]])(using Quotes): Expr[Any] = import MirroredExpr.* // TODO where we could define this? val mirroredLabel: Option[String] = mirror.mirroredLabel val mirroredElemLabels: Option[List[String]] = mirror.mirroredElemLabels val mirroredMonoType: Option[Type[?]] = mirror.mirroredMonoType val mirroredType: Option[Type[?]] = mirror.mirroredType val mirroredElemTypes: Option[List[Type[?]]] = mirror.mirroredElemTypes ... ``` [skip community_build] [skip docs] [skip mima]
2 parents 49b685d + 9fefcf6 commit 6acaf95

File tree

4 files changed

+109
-0
lines changed

4 files changed

+109
-0
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(Foo,List(x, y, z),Foo[scala.Long],Foo[scala.Long],List(scala.Int, scala.Double, scala.Long))
2+
(Foo,List(x, y, z),Foo[scala.Long],Foo[scala.Long],List(scala.Int, scala.Double, scala.Long))
3+
(Bar,List(A, B),Bar,Bar,List(Bar.A.type, Bar.B))
4+
(Bar,List(A, B),Bar,Bar,List(Bar.A.type, Bar.B))
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import scala.deriving.Mirror
2+
import scala.quoted.*
3+
4+
object MirroredExpr:
5+
extension (mirror: Expr[Mirror])
6+
def mirroredMonoType(using Quotes): Option[Type[?]] =
7+
mirror match
8+
case '{ $_ : Mirror { type MirroredMonoType = t } } => Some(Type.of[t])
9+
case _ => None
10+
11+
def mirroredType(using Quotes): Option[Type[?]] =
12+
mirror match
13+
case '{ $_ : Mirror { type MirroredType = t } } => Some(Type.of[t])
14+
case _ => None
15+
16+
def mirroredLabel(using Quotes): Option[String] =
17+
mirror match
18+
case '{ type label <: String; $_ : Mirror { type MirroredLabel = label } } =>
19+
Type.valueOfConstant[label]
20+
case _ => None
21+
22+
def mirroredElemTypes(using Quotes): Option[List[Type[?]]] =
23+
mirror match
24+
case '{ type labels <: Tuple; $_ : Mirror { type MirroredElemTypes = labels } } =>
25+
tupleTypes[labels]
26+
case _ => None
27+
28+
def mirroredElemLabels(using Quotes): Option[List[String]] =
29+
mirror match
30+
case '{ type labels <: Tuple; $_ : Mirror { type MirroredElemLabels = labels } } =>
31+
Type.valueOfTuple[labels].map(_.toList.asInstanceOf[List[String]])
32+
case _ => None
33+
34+
private def tupleTypes[T <: Tuple : Type](using Quotes): Option[List[Type[?]]] =
35+
import quotes.reflect.*
36+
val cons = Symbol.classSymbol("scala.*:")
37+
def rec(tpe: TypeRepr): Option[List[Type[?]]] =
38+
tpe.widenTermRefByName.dealias match
39+
case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) =>
40+
tpes.foldRight(Option(List.empty[Type[?]])) {
41+
case (_, None) => None
42+
case (tpe, Some(acc)) => Some(tpe.asType :: acc)
43+
case _ => None
44+
}
45+
case AppliedType(tp, List(headType, tail)) if tp.derivesFrom(cons) =>
46+
rec(tail) match
47+
case Some(tailTypes) => Some(headType.asType :: tailTypes)
48+
case None => None
49+
case tpe =>
50+
if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then Some(Nil)
51+
else None
52+
rec(TypeRepr.of[T])
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import scala.deriving.Mirror
2+
import scala.quoted.*
3+
4+
inline def reflectMirrorInfo[T](using mirror: Mirror.Of[T]): Any = ${ reflectMirrorInfoExpr[T]('mirror) }
5+
6+
private def reflectMirrorInfoExpr[T: Type](mirror: Expr[Mirror.Of[T]])(using Quotes): Expr[Any] =
7+
val mirroredLabel: String = MirroredExpr.mirroredLabel(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredLabel not found"))
8+
val mirroredElemLabels = MirroredExpr.mirroredElemLabels(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemLabels not found"))
9+
val mirroredMonoType: Type[?] = MirroredExpr.mirroredMonoType(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredMonoType not found"))
10+
val mirroredType: Type[?] = MirroredExpr.mirroredType(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredType not found"))
11+
val mirroredElemTypes: List[Type[?]] = MirroredExpr.mirroredElemTypes(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemTypes not found"))
12+
13+
val mirroredMonoTypeString = mirroredMonoType match
14+
case '[t] => Type.show[t]
15+
val mirroredTypeString = mirroredType match
16+
case '[t] => Type.show[t]
17+
val mirroredElemTypesStrings = mirroredElemTypes.map {
18+
case '[t] => Type.show[t]
19+
}
20+
21+
Expr((mirroredLabel, mirroredElemLabels, mirroredMonoTypeString, mirroredTypeString, mirroredElemTypesStrings))
22+
23+
inline def reflectMirrorInfo2[T](using mirror: Mirror.Of[T]): Any = ${ reflectMirrorInfoExpr2[T]('mirror) }
24+
25+
private def reflectMirrorInfoExpr2[T: Type](mirror: Expr[Mirror.Of[T]])(using Quotes): Expr[Any] =
26+
import MirroredExpr.*
27+
val mirroredLabel: String = mirror.mirroredLabel.getOrElse(quotes.reflect.report.errorAndAbort("MirroredLabel not found"))
28+
val mirroredElemLabels = mirror.mirroredElemLabels.getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemLabels not found"))
29+
val mirroredMonoType: Type[?] = mirror.mirroredMonoType.getOrElse(quotes.reflect.report.errorAndAbort("MirroredMonoType not found"))
30+
val mirroredType: Type[?] = mirror.mirroredType.getOrElse(quotes.reflect.report.errorAndAbort("MirroredType not found"))
31+
val mirroredElemTypes: List[Type[?]] = mirror.mirroredElemTypes.getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemTypes not found"))
32+
33+
val mirroredMonoTypeString = mirroredMonoType match
34+
case '[t] => Type.show[t]
35+
val mirroredTypeString = mirroredType match
36+
case '[t] => Type.show[t]
37+
val mirroredElemTypesStrings = mirroredElemTypes.map {
38+
case '[t] => Type.show[t]
39+
}
40+
41+
Expr((mirroredLabel, mirroredElemLabels, mirroredMonoTypeString, mirroredTypeString, mirroredElemTypesStrings))
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import scala.deriving.Mirror
2+
3+
case class Foo[T](x: Int, y: Double, z: T)
4+
enum Bar:
5+
case A
6+
case B(b: Int)
7+
8+
@main def Test: Unit =
9+
println(reflectMirrorInfo[Foo[Long]])
10+
println(reflectMirrorInfo2[Foo[Long]])
11+
println(reflectMirrorInfo[Bar])
12+
println(reflectMirrorInfo2[Bar])

0 commit comments

Comments
 (0)