从inferImplicitValue返回的Scala语法树未能评估

问题描述:

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

// 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

所以,你可以看到它找到了声明的树d隐含值demo.this.demoEvidence,但对该树的评估失败。我已经看到这个基本方法在我的项目的其他地方工作。不知道有什么区别,为什么它在这里失败。

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

所以问题是,这只是我必须忍受的一个基本约束,或者是否有一些聪明的解决方法,或者这是一个“错误”,推断可能固定的宏内的隐式值。

更新:我提交了Scala的问题本:https://github.com/scala/scala-dev/issues/353

+0

您至少可以包含堆栈跟踪以及异常的'toString'。 –

+0

我没有得到任何跟踪。但是我会尝试删除try/catch并查看我得到的结果 – eje

+1

或者只是在'catch'中使用'e.printStackTrace'。 –

从堆栈跟踪看,eval期待object demo在类文件的形式执行,这是有道理的给予存在,你是值试图计算取决于val demoEvidence这是object demo的成员。

eval发生在的类型检查过程中,因此类文件尚不存在,因此错误。在子项目中定义隐含值的版本中,我认为子项目是首先编译的,因此存在eval所需的类文件,因此评估按照您的预期进行。

+0

我可以看到如何导致一个问题。它使'inferImplicitValue'与常规的隐式参数有点不对称,因为只是在该场景下用隐式参数_does_声明'foo'。在一个完美的世界中,我希望'inferImplicitValue'会在宏内返回一个等价于'q“new DemoEvidence(42)”的树。理论上,它似乎应该在当前宏观背景下的某个地方可用。不确定这将是多么可行。 – eje

+0

在刚描述过的非宏案例中,没有对'eval'的调用,所以不需要编译代码来执行......问题全在'eval'中。 –