Skip to content

Commit 086d1a8

Browse files
Merge pull request #8343 from dotty-staging/add-quoted-liftable-macro-derivation
Add quoted liftable macro derivation regression test
2 parents 80e1920 + c3fc7dc commit 086d1a8

File tree

3 files changed

+93
-0
lines changed

3 files changed

+93
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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 Type[T], Quotes): Expr[T] // TODO remove `Type[T]`
7+
8+
object Lft {
9+
given Lft[Int] with
10+
def toExpr(x: Int)(using Type[Int], 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 qctx: Quotes, tpe: Type[T]): Expr[Lft[T]] = {
15+
mirrorExpr match {
16+
case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
17+
val liftables = Expr.ofSeq(elemTypesLfts[mirroredElemTypes])
18+
'{ new LiftableSum[T, mirroredElemTypes]($mirrorExpr, $liftables) }
19+
case '{ $mirrorExpr : Mirror.Product { type MirroredElemTypes = mirroredElemTypes } } =>
20+
val liftableExprs = Expr.ofSeq(elemTypesLfts[mirroredElemTypes])
21+
'{ new LiftableProduct[T, mirroredElemTypes]($mirrorExpr, $liftableExprs) }
22+
}
23+
}
24+
25+
class LiftableSum[T, MElemTypes](
26+
mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T },
27+
liftables: Seq[Lft[_]] // TODO make Lft creation lazy
28+
) extends Lft[T]:
29+
def toExpr(x: T)(using Type[T], Quotes): Expr[T] =
30+
val ordinal = mirror.ordinal(x)
31+
val tp = Expr.summon[Mirror.SumOf[T]].get match
32+
case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } =>
33+
elemType[mirroredElemTypes](ordinal)
34+
val liftable = liftables.apply(ordinal).asInstanceOf[Lft[T]]
35+
liftable.toExpr(x)(using tp.asInstanceOf[Type[T]], summon[Quotes])
36+
end LiftableSum
37+
38+
class LiftableProduct[T, MElemTypes](
39+
mirror: Mirror.Product { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T },
40+
liftables: Seq[Lft[_]]
41+
) extends Lft[T]:
42+
def toExpr(x: T)(using Type[T], Quotes): Expr[T] =
43+
val mirrorExpr = Expr.summon[Mirror.ProductOf[T]].get
44+
val elemExprs =
45+
x.asInstanceOf[Product].productIterator.zip(liftables.iterator).map { (elem, lift) =>
46+
lift.asInstanceOf[Lft[Any]].toExpr(elem)
47+
}.toSeq
48+
val elemsTupleExpr = Expr.ofTupleFromSeq(elemExprs)
49+
'{ $mirrorExpr.fromProduct($elemsTupleExpr) }
50+
end LiftableProduct
51+
52+
private def elemTypesLfts[X: Type](using Quotes): List[Expr[Lft[_]]] =
53+
Type.of[X] match
54+
case '[ head *: tail ] =>
55+
Expr.summon[Lft[head]].getOrElse(quotes.reflect.report.throwError(s"Could not find given Lft[${Type.show[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+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
sealed trait Opt[+T] derives Lft
3+
case class Sm[T](t: T) extends Opt[T] derives Lft
4+
case object Nn extends Opt[Nothing] derives Lft
5+
6+
object Lib {
7+
8+
import scala.quoted._
9+
import Opt.*
10+
11+
inline def optTwo = ${optTwoExpr}
12+
inline def smTwo = ${smTwoExpr}
13+
inline def none = ${noneExpr}
14+
15+
private def optTwoExpr(using Quotes): Expr[Opt[Int]] =
16+
summon[Lft[Opt[Int]]].toExpr(Sm(2))
17+
18+
private def smTwoExpr(using Quotes): Expr[Sm[Int]] =
19+
summon[Lft[Sm[Int]]].toExpr(Sm(2))
20+
21+
private def noneExpr(using Quotes): Expr[Opt[Int]] =
22+
summon[Lft[Nn.type]].toExpr(Nn)
23+
}
24+
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)