2

我正在编写一个 Scala 宏(Scala 2.11),我想在其中使用 获取表示宏内部隐式变量的inferImplicitValue树,评估该语法树,然后使用该值。我实际上已经这样做了,但它似乎并不适用于所有情况[1]。我构建了一个失败的简化示例。

// a class for implicit evidence
class DemoEvidence(val value: Int)

// define 'foo' method for invoking the macro
object demoModule {
  def foo: Int = macro DemoMacros.fooImpl
}

class DemoMacros(val c: whitebox.Context) {
  import c.universe._

  def fooImpl: Tree = {
    val vInt = try {
      // get the tree representing the implicit value
      val impl = c.inferImplicitValue(typeOf[DemoEvidence], silent = false)
      // print it out
      println(s"impl= $impl")
      // try to evaluate the tree (this is failing)
      val eval = c.eval(c.Expr[DemoEvidence](c.untypecheck(impl.duplicate)))
      eval.value
    } catch {
      case e: Throwable => {
        // on failure print out the failure message
        println(s"Eval failed with: $e\nStack trace:\n${e.printStackTrace}")
        0
      }
    }
    q"$vInt"  // return tree representing the integer value
  }
}

如果我编译上述内容,然后调用它:

object demo {
  implicit val demoEvidence: DemoEvidence = new DemoEvidence(42)
  val i: Int = demoModule.foo
}

我看到编译失败的方式如下:

impl= demo.this.demoEvidence
java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at scala.tools.reflect.ToolBoxFactory$ToolBoxImpl$ToolBoxGlobal$$anonfun$compile$1.apply(ToolBoxFactory.scala:275)
...

完整输出: https ://gist.github.com/erikerlandson/df48f64329be6ab9de9caef5f5be4a83

因此,您可以看到它正在为声明的隐式 value 查找树demo.this.demoEvidence,但对该树的评估失败。我已经在我的项目的其他地方看到了这种基本方法。不知道有什么区别,以及为什么在这里失败。

[1] 更新:如果隐式值在(子)项目中定义,并编译,然后在该项目外部使用,它会按预期工作。这就是这种方法对我有用的情况。

所以问题是这是否只是我必须忍受的一个基本约束,或者是否有一些巧妙的解决方法,或者这是否是一个“错误”,可以在宏中推断出可能修复的隐式值。

更新:我为此提交了一个 Scala 问题:https ://github.com/scala/scala-dev/issues/353

4

1 回答 1

1

从堆栈跟踪的外观来看,eval期望object demo以类文件形式存在以供执行,考虑到您尝试计算的值取决于val demoEvidence哪个是object demo.

但是在类型检查eval期间发生了这种情况,object demo因此类文件尚不存在,因此出现错误。在子项目中定义了隐式值的版本中,我想首先编译子项目,因此eval存在所需的类文件,因此评估会按您的预期进行。

于 2017-04-03T07:46:35.223 回答