forked from scala/scala3
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add portotype for Expr[Mirror] methods (scala#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]
- Loading branch information
Showing
4 changed files
with
109 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |