Scala宏处理non-trivial lambda出现Macro compilation error问题求助
错误原因分析
该编译错误由Scala AST的符号所有者(Owner)链不匹配导致,具体逻辑如下:
- 你通过模式匹配
val Function(params, body) = func.tree解构传入的lambda AST时,body内的所有自定义局部变量(比如示例中的temp)、参数引用的符号都绑定了所有者:所有者是原lambda对应的方法符号。 - 你构造新的
Function(params, body)节点作为返回值时,新的Function节点会生成一个全新的方法符号,而你直接复用的原params、原body中所有符号的所有者仍然是旧的原lambda方法符号,和新Function的符号没有关联。 - 简单lambda(比如
Macro.lit(_ + 19))的body中没有自定义局部变量,参数引用的绑定逻辑不会校验所有者链,因此运行正常;一旦存在局部变量定义,字节码生成阶段需要根据符号的所有者查找局部变量的栈帧槽位时,就会找不到对应的temp变量,抛出该错误。
修复方案
你可以通过Scala反射API提供的internal.changeOwner方法,批量修改原body内所有符号的所有者为新Function的符号即可解决问题,参考实现:
object Macro { def literalInt(c: blackbox.Context)(func: c.Expr[Int => Int]): c.Expr[Int => Int] = { import c.universe._ val oldFunc @ Function(oldParams, oldBody) = func.tree // 复制参数定义生成新的参数符号 val newParams = oldParams.map(p => treeCopy.ValDef(p, p.mods, p.name, p.tpt, p.rhs)) // 批量修改body内所有符号的所有者为新的Function val newFunc = Function(newParams, oldBody) val fixedBody = c.internal.changeOwner(oldBody, oldFunc.symbol, newFunc.symbol) c.Expr(Function(newParams, fixedBody)) } def lit(func: Int => Int): Int => Int = macro literalInt }
内容的提问来源于stack exchange,提问作者Piotr Jósiak
相关产品推荐
相关产品推荐

