我想将ValDef具体化到运行时,但它不能直接工作。如果我将ValDef封装到一个Block中,一切都可以完美地工作,就像下面的例子一样:
case class Container(expr: Expr[Any])
def lift(expr: Any): Container = macro reifyValDef
def reifyValDef(c: Context)(expr: c.Expr[Any]): c.Expr[Container] = {
import c.universe._
expr.tree match {
case Block(List(v: ValDef), _) =>
val asBlock = q"{$v}"
val toRuntime = q"scala.reflect.runtime.universe.reify($asBlock)"
c.Expr[Container](q"Container($toRuntime)")
}
}
lift {
val x: Int = 10
}
如果我直接使用v,而不是将其包装到一个块中,我会得到错误:
<>之前错误:(10,11)类型不匹配;发现:要求:任何注意,它扩展Any,而不是AnyRef。这些类型可以参与值类,但不能参与实例不能出现在单例类型或引用比较中。val x: Int = 10^之前它只是不直接与ValDefs工作还是我的代码有问题?
这是反射API中已知的问题之一。定义在技术上不是表达式,所以你不能直接将它们作为参数传递给函数。将定义包装在块中是对块寻址的正确方法。
错误信息当然令人困惑,但它确实有一些扭曲的意义。为了表示定义本身没有类型,将相应的Tree
的tpe
字段设置为NoType
。然后根据Any
检查宏参数的类型,检查失败(因为NoType
是一种特殊类型,它与任何东西都不兼容),因此打印一条标准错误消息。奇怪的打印输出是漂亮打印机在这种奇怪情况下的行为。