From c3fc7dc6889aed9ff34ed0dfb1054220270d0b0b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 13 Feb 2020 10:24:30 +0100 Subject: [PATCH] Add quoted Liftable derivation --- .../Derivation_1.scala | 63 +++++++++++++++++++ .../Lib_2.scala | 24 +++++++ .../Test_3.scala | 6 ++ 3 files changed, 93 insertions(+) create mode 100644 tests/run-macros/quoted-liftable-derivation-macro/Derivation_1.scala create mode 100644 tests/run-macros/quoted-liftable-derivation-macro/Lib_2.scala create mode 100644 tests/run-macros/quoted-liftable-derivation-macro/Test_3.scala diff --git a/tests/run-macros/quoted-liftable-derivation-macro/Derivation_1.scala b/tests/run-macros/quoted-liftable-derivation-macro/Derivation_1.scala new file mode 100644 index 000000000000..059ebc16e446 --- /dev/null +++ b/tests/run-macros/quoted-liftable-derivation-macro/Derivation_1.scala @@ -0,0 +1,63 @@ +import scala.compiletime.{erasedValue, summonFrom} +import scala.deriving._ +import scala.quoted._ + +trait Lft[T]: + def toExpr(x: T)(using Type[T], Quotes): Expr[T] // TODO remove `Type[T]` + +object Lft { + given Lft[Int] with + def toExpr(x: Int)(using Type[Int], Quotes) = Expr(x) + + inline given derived[T](using inline m: Mirror.Of[T]): Lft[T] = ${ derivedExpr('m) } + + private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using qctx: Quotes, tpe: Type[T]): Expr[Lft[T]] = { + mirrorExpr match { + case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => + val liftables = Expr.ofSeq(elemTypesLfts[mirroredElemTypes]) + '{ new LiftableSum[T, mirroredElemTypes]($mirrorExpr, $liftables) } + case '{ $mirrorExpr : Mirror.Product { type MirroredElemTypes = mirroredElemTypes } } => + val liftableExprs = Expr.ofSeq(elemTypesLfts[mirroredElemTypes]) + '{ new LiftableProduct[T, mirroredElemTypes]($mirrorExpr, $liftableExprs) } + } + } + + class LiftableSum[T, MElemTypes]( + mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T }, + liftables: Seq[Lft[_]] // TODO make Lft creation lazy + ) extends Lft[T]: + def toExpr(x: T)(using Type[T], Quotes): Expr[T] = + val ordinal = mirror.ordinal(x) + val tp = Expr.summon[Mirror.SumOf[T]].get match + case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => + elemType[mirroredElemTypes](ordinal) + val liftable = liftables.apply(ordinal).asInstanceOf[Lft[T]] + liftable.toExpr(x)(using tp.asInstanceOf[Type[T]], summon[Quotes]) + end LiftableSum + + class LiftableProduct[T, MElemTypes]( + mirror: Mirror.Product { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T }, + liftables: Seq[Lft[_]] + ) extends Lft[T]: + def toExpr(x: T)(using Type[T], Quotes): Expr[T] = + val mirrorExpr = Expr.summon[Mirror.ProductOf[T]].get + val elemExprs = + x.asInstanceOf[Product].productIterator.zip(liftables.iterator).map { (elem, lift) => + lift.asInstanceOf[Lft[Any]].toExpr(elem) + }.toSeq + val elemsTupleExpr = Expr.ofTupleFromSeq(elemExprs) + '{ $mirrorExpr.fromProduct($elemsTupleExpr) } + end LiftableProduct + + private def elemTypesLfts[X: Type](using Quotes): List[Expr[Lft[_]]] = + Type.of[X] match + case '[ head *: tail ] => + Expr.summon[Lft[head]].getOrElse(quotes.reflect.report.throwError(s"Could not find given Lft[${Type.show[head]}]")) :: elemTypesLfts[tail] + case '[ EmptyTuple ] => Nil + + private def elemType[X: Type](ordinal: Int)(using Quotes): Type[_] = + Type.of[X] match + case '[ head *: tail ] => + if ordinal == 0 then Type.of[head] + else elemType[tail](ordinal - 1) +} diff --git a/tests/run-macros/quoted-liftable-derivation-macro/Lib_2.scala b/tests/run-macros/quoted-liftable-derivation-macro/Lib_2.scala new file mode 100644 index 000000000000..a5e3ea9d7cef --- /dev/null +++ b/tests/run-macros/quoted-liftable-derivation-macro/Lib_2.scala @@ -0,0 +1,24 @@ + +sealed trait Opt[+T] derives Lft +case class Sm[T](t: T) extends Opt[T] derives Lft +case object Nn extends Opt[Nothing] derives Lft + +object Lib { + + import scala.quoted._ + import Opt.* + + inline def optTwo = ${optTwoExpr} + inline def smTwo = ${smTwoExpr} + inline def none = ${noneExpr} + + private def optTwoExpr(using Quotes): Expr[Opt[Int]] = + summon[Lft[Opt[Int]]].toExpr(Sm(2)) + + private def smTwoExpr(using Quotes): Expr[Sm[Int]] = + summon[Lft[Sm[Int]]].toExpr(Sm(2)) + + private def noneExpr(using Quotes): Expr[Opt[Int]] = + summon[Lft[Nn.type]].toExpr(Nn) +} + diff --git a/tests/run-macros/quoted-liftable-derivation-macro/Test_3.scala b/tests/run-macros/quoted-liftable-derivation-macro/Test_3.scala new file mode 100644 index 000000000000..3ba5e8d2c3e7 --- /dev/null +++ b/tests/run-macros/quoted-liftable-derivation-macro/Test_3.scala @@ -0,0 +1,6 @@ +object Test extends App { + import Opt._ + assert(Lib.optTwo == Sm(2)) + assert(Lib.smTwo == Sm(2)) + assert(Lib.none == Nn) +}