Skip to content

Commit 220625f

Browse files
committed
Quoted liftable derivation macro regression test
1 parent 50db81d commit 220625f

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import scala.compiletime.{erasedValue, summonFrom}
2+
import scala.deriving._
3+
import scala.quoted._
4+
5+
trait Lft[T]:
6+
def toExpr(x: T)(using Quotes): Expr[T]
7+
8+
object Lft {
9+
given Lft[Int] with
10+
def toExpr(x: Int)(using Quotes) = Expr(x)
11+
12+
inline given derived[T](using inline m: Mirror.Of[T]): Lft[T] = ${ derivedExpr('m) }
13+
14+
private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using Quotes, Type[T]): Expr[Lft[T]] = {
15+
val tpe = summonExprOrError[Type[T]]
16+
mirrorExpr match {
17+
case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
18+
val liftables = elemTypesLfts[mirroredElemTypes]
19+
val liftablesFn = '{ (x: Int) => ${ switchExpr('x, liftables) } }
20+
'{ new LiftableSum[T, mirroredElemTypes]($mirrorExpr, $liftablesFn)(using $tpe) }
21+
case '{ $mirrorExpr : Mirror.Product { type MirroredElemTypes = mirroredElemTypes } } =>
22+
val liftableExprs = Expr.ofSeq(elemTypesLfts[mirroredElemTypes])
23+
'{ new LiftableProduct[T, mirroredElemTypes]($mirrorExpr, $liftableExprs)(using $tpe) }
24+
}
25+
}
26+
27+
class LiftableSum[T, MElemTypes](
28+
mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T },
29+
liftables: Int => Lft[_]
30+
)(using Type[T]) extends Lft[T]:
31+
def toExpr(x: T)(using Quotes): Expr[T] =
32+
val ordinal = mirror.ordinal(x)
33+
val liftable = liftables.apply(ordinal).asInstanceOf[Lft[T]]
34+
liftable.toExpr(x)
35+
end LiftableSum
36+
37+
class LiftableProduct[T, MElemTypes](
38+
mirror: Mirror.Product { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T },
39+
liftables: Seq[Lft[_]]
40+
)(using Type[T]) extends Lft[T]:
41+
def toExpr(x: T)(using Quotes): Expr[T] =
42+
val mirrorExpr = summonExprOrError[Mirror.ProductOf[T]]
43+
val xProduct = x.asInstanceOf[Product]
44+
val anyLiftables = liftables.asInstanceOf[Seq[Lft[Any]]]
45+
val elemExprs =
46+
xProduct.productIterator.zip(anyLiftables.iterator).map {
47+
(elem, lift) => lift.toExpr(elem)
48+
}.toSeq
49+
val elemsTupleExpr = Expr.ofTupleFromSeq(elemExprs)
50+
'{ $mirrorExpr.fromProduct($elemsTupleExpr) }
51+
end LiftableProduct
52+
53+
private def elemTypesLfts[X: Type](using Quotes): List[Expr[Lft[_]]] =
54+
Type.of[X] match
55+
case '[ head *: tail ] => summonExprOrError[Lft[head]] :: elemTypesLfts[tail]
56+
case '[ EmptyTuple ] => Nil
57+
58+
private def elemType[X: Type](ordinal: Int)(using Quotes): Type[_] =
59+
Type.of[X] match
60+
case '[ head *: tail ] =>
61+
if ordinal == 0 then Type.of[head]
62+
else elemType[tail](ordinal - 1)
63+
64+
private def summonExprOrError[T: Type](using Quotes): Expr[T] =
65+
Expr.summon[T] match
66+
case Some(expr) => expr
67+
case None =>
68+
quotes.reflect.report.throwError(s"Could not find implicit ${Type.show[T]}")
69+
70+
private def switchExpr(scrutinee: Expr[Int], seq: List[Expr[Lft[_]]])(using Quotes): Expr[Lft[_]] =
71+
import quotes.reflect._
72+
val cases = seq.zipWithIndex.map {
73+
(expr, i) => CaseDef(Literal(IntConstant(i)), None, expr.asTerm)
74+
}
75+
Match(scrutinee.asTerm, cases).asExprOf[Lft[_]]
76+
77+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
sealed trait Opt[+T]
3+
object Opt:
4+
inline given [T]: Lft[Opt[T]] = Lft.derived
5+
6+
case class Sm[T](t: T) extends Opt[T]
7+
object Sm:
8+
inline given [T]: Lft[Sm[T]] = Lft.derived
9+
10+
case object Nn extends Opt[Nothing]:
11+
inline given Lft[Nn.type] = Lft.derived
12+
13+
object Lib {
14+
15+
import scala.quoted._
16+
import Opt.*
17+
18+
inline def optTwo = ${optTwoExpr}
19+
inline def smTwo = ${smTwoExpr}
20+
inline def none = ${noneExpr}
21+
22+
private def optTwoExpr(using Quotes): Expr[Opt[Int]] =
23+
summon[Lft[Opt[Int]]].toExpr(Sm(2))
24+
25+
private def smTwoExpr(using Quotes): Expr[Sm[Int]] =
26+
summon[Lft[Sm[Int]]].toExpr(Sm(2))
27+
28+
private def noneExpr(using Quotes): Expr[Opt[Int]] =
29+
summon[Lft[Nn.type]].toExpr(Nn)
30+
}
31+
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
object Test extends App {
2+
import Opt._
3+
assert(Lib.optTwo == Sm(2))
4+
assert(Lib.smTwo == Sm(2))
5+
assert(Lib.none == Nn)
6+
}

0 commit comments

Comments
 (0)