Description
A full demonstration of this issue is available here (there is nothing else in the repo):
https://github.com/deusaquilus/mirror_test
minimized code
Let's say that I summon a mirror for an encoder via summonExpr inside a macro like so:
val mirrorTpe = '[Mirror.Of[$t]]
val mirrorExpr = summonExpr(given mirrorTpe) match {
case Some(mirror) => mirror
}
Then I proceed to use it to summon a derived type-class (e.g. JsonEncoder) like so:
'{
given JsonEncoder[T] = JsonEncoder.derived($mirrorExpr)
val encoder = summon[JsonEncoder[$t]]
encoder.encode($value)
}
Then I try to declare a class and use the macro containing the above summonExpr
:
case class PersonSimple(name:String, age:Int)
val stuff = PersonSimple("Joe", 123)
println(SummonJsonEncoderTest.encodeAndMessAroundType(stuff) )
Instead of summoning a Product-Type mirror, it summons the following mirror:
scala.deriving.Mirror{
MirroredType = t.$splice; MirroredMonoType = t.$splice;
MirroredElemTypes <: Tuple
}
Since this mirror does not contain any information about whether the type T
is a product or sum type, it is impossible to use. A typical encoder will fail with the following error:
[error] -- Error: /home/alexander/git/dotty/mirror_test/src/main/scala/org/deusaquilus/SummonJsonEncoderTest.scala:22:48
[error] 22 | given JsonEncoder[T] = JsonEncoder.derived($mirrorExpr)
[error] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[error] | cannot reduce inline match with
[error] | scrutinee: {
[error] | ev
[error] | } : (ev :
[error] | scala.deriving.Mirror{
[error] | MirroredType = t.$splice; MirroredMonoType = t.$splice;
[error] | MirroredElemTypes <: Tuple
[error] | }
[error] | )
[error] | patterns : case m @ _:deriving.Mirror.SumOf[T]
[error] | case m @ _:deriving.Mirror.ProductOf[T]
[error] | This location contains code that was inlined from JsonEncoder.scala:28
expectation
A Product mirror for the type PersonSimple
should be summoned that looks like this:
(
scala.deriving.Mirror{
MirroredType = PersonSimple; MirroredMonoType = PersonSimple;
MirroredElemTypes <: Tuple
}
&
scala.deriving.Mirror.Product{
MirroredMonoType = PersonSimple; MirroredType = PersonSimple;
MirroredLabel = "PersonSimple"
}
){MirroredElemTypes = (String, Int); MirroredElemLabels = ("name", "age")}
Edit:
Note that it is interesting that with summonFrom
this seems to work:
inline def encodeAndMessAroundType[T](value: =>T): String = {
summonFrom {
case m: Mirror.ProductOf[T] => derived(m).encode(value)
}
}
Full example here:
https://github.com/deusaquilus/mirror_test/tree/summonFrom
What is the difference between what I am doing with summonExpr
and summonFrom
?