Skip to content

Commit

Permalink
Add portotype for Expr[Mirror] methods (scala#18072)
Browse files Browse the repository at this point in the history
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]
  • Loading branch information
nicolasstucki authored Jun 27, 2023
2 parents 49b685d + 9fefcf6 commit 6acaf95
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 0 deletions.
4 changes: 4 additions & 0 deletions tests/run-macros/expr-mirror-info.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
(Foo,List(x, y, z),Foo[scala.Long],Foo[scala.Long],List(scala.Int, scala.Double, scala.Long))
(Foo,List(x, y, z),Foo[scala.Long],Foo[scala.Long],List(scala.Int, scala.Double, scala.Long))
(Bar,List(A, B),Bar,Bar,List(Bar.A.type, Bar.B))
(Bar,List(A, B),Bar,Bar,List(Bar.A.type, Bar.B))
52 changes: 52 additions & 0 deletions tests/run-macros/expr-mirror-info/Lib_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import scala.deriving.Mirror
import scala.quoted.*

object MirroredExpr:
extension (mirror: Expr[Mirror])
def mirroredMonoType(using Quotes): Option[Type[?]] =
mirror match
case '{ $_ : Mirror { type MirroredMonoType = t } } => Some(Type.of[t])
case _ => None

def mirroredType(using Quotes): Option[Type[?]] =
mirror match
case '{ $_ : Mirror { type MirroredType = t } } => Some(Type.of[t])
case _ => None

def mirroredLabel(using Quotes): Option[String] =
mirror match
case '{ type label <: String; $_ : Mirror { type MirroredLabel = label } } =>
Type.valueOfConstant[label]
case _ => None

def mirroredElemTypes(using Quotes): Option[List[Type[?]]] =
mirror match
case '{ type labels <: Tuple; $_ : Mirror { type MirroredElemTypes = labels } } =>
tupleTypes[labels]
case _ => None

def mirroredElemLabels(using Quotes): Option[List[String]] =
mirror match
case '{ type labels <: Tuple; $_ : Mirror { type MirroredElemLabels = labels } } =>
Type.valueOfTuple[labels].map(_.toList.asInstanceOf[List[String]])
case _ => None

private def tupleTypes[T <: Tuple : Type](using Quotes): Option[List[Type[?]]] =
import quotes.reflect.*
val cons = Symbol.classSymbol("scala.*:")
def rec(tpe: TypeRepr): Option[List[Type[?]]] =
tpe.widenTermRefByName.dealias match
case AppliedType(fn, tpes) if defn.isTupleClass(fn.typeSymbol) =>
tpes.foldRight(Option(List.empty[Type[?]])) {
case (_, None) => None
case (tpe, Some(acc)) => Some(tpe.asType :: acc)
case _ => None
}
case AppliedType(tp, List(headType, tail)) if tp.derivesFrom(cons) =>
rec(tail) match
case Some(tailTypes) => Some(headType.asType :: tailTypes)
case None => None
case tpe =>
if tpe.derivesFrom(Symbol.classSymbol("scala.EmptyTuple")) then Some(Nil)
else None
rec(TypeRepr.of[T])
41 changes: 41 additions & 0 deletions tests/run-macros/expr-mirror-info/Macro_1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import scala.deriving.Mirror
import scala.quoted.*

inline def reflectMirrorInfo[T](using mirror: Mirror.Of[T]): Any = ${ reflectMirrorInfoExpr[T]('mirror) }

private def reflectMirrorInfoExpr[T: Type](mirror: Expr[Mirror.Of[T]])(using Quotes): Expr[Any] =
val mirroredLabel: String = MirroredExpr.mirroredLabel(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredLabel not found"))
val mirroredElemLabels = MirroredExpr.mirroredElemLabels(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemLabels not found"))
val mirroredMonoType: Type[?] = MirroredExpr.mirroredMonoType(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredMonoType not found"))
val mirroredType: Type[?] = MirroredExpr.mirroredType(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredType not found"))
val mirroredElemTypes: List[Type[?]] = MirroredExpr.mirroredElemTypes(mirror).getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemTypes not found"))

val mirroredMonoTypeString = mirroredMonoType match
case '[t] => Type.show[t]
val mirroredTypeString = mirroredType match
case '[t] => Type.show[t]
val mirroredElemTypesStrings = mirroredElemTypes.map {
case '[t] => Type.show[t]
}

Expr((mirroredLabel, mirroredElemLabels, mirroredMonoTypeString, mirroredTypeString, mirroredElemTypesStrings))

inline def reflectMirrorInfo2[T](using mirror: Mirror.Of[T]): Any = ${ reflectMirrorInfoExpr2[T]('mirror) }

private def reflectMirrorInfoExpr2[T: Type](mirror: Expr[Mirror.Of[T]])(using Quotes): Expr[Any] =
import MirroredExpr.*
val mirroredLabel: String = mirror.mirroredLabel.getOrElse(quotes.reflect.report.errorAndAbort("MirroredLabel not found"))
val mirroredElemLabels = mirror.mirroredElemLabels.getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemLabels not found"))
val mirroredMonoType: Type[?] = mirror.mirroredMonoType.getOrElse(quotes.reflect.report.errorAndAbort("MirroredMonoType not found"))
val mirroredType: Type[?] = mirror.mirroredType.getOrElse(quotes.reflect.report.errorAndAbort("MirroredType not found"))
val mirroredElemTypes: List[Type[?]] = mirror.mirroredElemTypes.getOrElse(quotes.reflect.report.errorAndAbort("MirroredElemTypes not found"))

val mirroredMonoTypeString = mirroredMonoType match
case '[t] => Type.show[t]
val mirroredTypeString = mirroredType match
case '[t] => Type.show[t]
val mirroredElemTypesStrings = mirroredElemTypes.map {
case '[t] => Type.show[t]
}

Expr((mirroredLabel, mirroredElemLabels, mirroredMonoTypeString, mirroredTypeString, mirroredElemTypesStrings))
12 changes: 12 additions & 0 deletions tests/run-macros/expr-mirror-info/Test_2.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.deriving.Mirror

case class Foo[T](x: Int, y: Double, z: T)
enum Bar:
case A
case B(b: Int)

@main def Test: Unit =
println(reflectMirrorInfo[Foo[Long]])
println(reflectMirrorInfo2[Foo[Long]])
println(reflectMirrorInfo[Bar])
println(reflectMirrorInfo2[Bar])

0 comments on commit 6acaf95

Please sign in to comment.