I’m writing macro that extracts all guards from match case in form of List[A => Boolean] (repository: GitHub - Lifeblossom/scala-macro-bug-report). It looks like this
import scala.reflect.macros.whitebox
class MacroImpl(val c: whitebox.Context) {
import c.universe._
def derive[T: WeakTypeTag](pf: MacroImpl.this.c.Tree): Tree = {
val functions = pf
.collect { case c: CaseDef => c}
.map {
case CaseDef(literal: Literal, EmptyTree, _) =>
q"(t: ${weakTypeTag[T]}) => t == ${literal}"
case CaseDef(Bind(term, Ident(termNames.WILDCARD)), guard, _) =>
q"(${term.toTermName}: ${weakTypeTag[T]}) => ${guard}"
case cf =>
throw new RuntimeException(
s"Cannot handle case [${cf}]. Raw tree [${showRaw(cf)}]"
)
}
println(s"RES: [${functions}]")
println(s"TREE 1: ${showRaw(functions(0))}")
println(s"TREE 2: ${showRaw(functions(1))}")
println(functions(0).equalsStructure(functions(1)))
q"Seq(..${functions})"
}
}
I’m testing it with following test:
object Test {
val functions = Macro.magicMatch[String, String]({
case "abc" => "Case 1"
case t if t == "abc" => "Case 2"
case "abc" => "Case 3"
case t if t == "abc" => "Case 4"
})
}
Every returned A => Boolean function has the same tree: Function(List(ValDef(Modifiers(PARAM), TermName("t"), TypeTree(), EmptyTree)), Apply(Select(Ident(TermName("t")), TermName("$eq$eq")), List(Literal(Constant("abc")))))
which transforms to the same bytecode i.e
final <artifact> def $anonfun$functions(t: String): Boolean = t.==("abc");
((t: String) => $anonfun$functions(t))
}, {
final <artifact> def $anonfun$functions(t: String): Boolean = t.==("abc");
((t: String) => $anonfun$functions(t))
}, {
final <artifact> def $anonfun$functions(t: String): Boolean = t.==("abc");
((t: String) => $anonfun$functions(t))
}, {
final <artifact> def $anonfun$functions(t: String): Boolean = t.==("abc");
((t: String) => $anonfun$functions(t))
}}));
after uncurry stage. But after lambdalift they differ
final <artifact> private[this] def $anonfun$functions$1(t: String): Boolean = t.==("abc");
final <artifact> private[this] def $anonfun$functions$2(t$1: String, t: String): Boolean = t$1.==("abc");
final <artifact> private[this] def $anonfun$functions$3(t: String): Boolean = t.==("abc");
final <artifact> private[this] def $anonfun$functions$4(t$2: String, t: String): Boolean = t$2.==("abc")
while in theory it shouldn’t cause any problems (calls are changed accordingly) the whole compilation fails on
[error] Error while emitting Test.scala
[error] value t
Does it mean that despite having being the same, trees extracted from case t if t == "abc"
has some hidden properties that only lambdalift uses?