diff --git a/tests/run-macros/expr-mirror-info.check b/tests/run-macros/expr-mirror-info.check new file mode 100644 index 000000000000..c851c720caa1 --- /dev/null +++ b/tests/run-macros/expr-mirror-info.check @@ -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)) diff --git a/tests/run-macros/expr-mirror-info/Lib_1.scala b/tests/run-macros/expr-mirror-info/Lib_1.scala new file mode 100644 index 000000000000..d4d50ce71012 --- /dev/null +++ b/tests/run-macros/expr-mirror-info/Lib_1.scala @@ -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]) diff --git a/tests/run-macros/expr-mirror-info/Macro_1.scala b/tests/run-macros/expr-mirror-info/Macro_1.scala new file mode 100644 index 000000000000..eea6bade48ca --- /dev/null +++ b/tests/run-macros/expr-mirror-info/Macro_1.scala @@ -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)) diff --git a/tests/run-macros/expr-mirror-info/Test_2.scala b/tests/run-macros/expr-mirror-info/Test_2.scala new file mode 100644 index 000000000000..0b72d0032bd1 --- /dev/null +++ b/tests/run-macros/expr-mirror-info/Test_2.scala @@ -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])