I am writing a Scala macro (Scala 2.11) where I'd like to obtain the tree representing an implicit variable inside the macro using inferImplicitValue
, evaluate that syntax tree, and use the value. I have actually done this, but it doesn't seem to work in all circumstances[1]. I constructed a simplified example where it fails.
// 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
}
}
If I compile the above, and then invoke it:
object demo {
implicit val demoEvidence: DemoEvidence = new DemoEvidence(42)
val i: Int = demoModule.foo
}
I see the compilation fail in the following way:
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)
...
Full output at: https://gist.github.com/erikerlandson/df48f64329be6ab9de9caef5f5be4a83
So, you can see it is finding the tree for the declared implicit value demo.this.demoEvidence
, but evaluation of that tree is failing. I have seen this basic approach work elsewhere in my project. Not sure what the difference is, and why it fails here.
[1] UPDATE: If the implicit value is defined in a (sub)project, and compiled, and then used exterior to that project, it works as expected. That was the case where this approach is working for me.
So the question is whether that's just a fundamental constraint I have to live with, or if there is some clever workaround, or if this is a "bug" with inferring implicit values inside macros that might be fixed.
UPDATE: I filed a Scala issue for this: https://github.com/scala/scala-dev/issues/353