paradigmatic
paradigmatic

Reputation: 40461

How to evaluate an expression inside a Scala macro?

I'm trying to evaluate an Expr inside a macro using the Context#eval method:

//Dummy implementation
def evalArrayTree(c: Context)(a: c.Expr[ArrayTree]): c.Expr[Array[Double]] = {
  import c.universe._
  println( c.eval(a) )

  val tree = reify( Array(0.0,0.0,0.0) ).tree 
  c.Expr[Array[Double]]( tree )
}

However, the compiler complains with:

[error] /home/falcone/prg/sbt-example-paradise/core/src/main/scala/Test.scala:20: exception during macro expansion: 
[error] scala.tools.reflect.ToolBoxError: reflective toolbox has failed

If found in the scala-user ML, that the problem could be solved using resetAllAttrs. However

  1. I don't understand how I am supposed to use it.
  2. This function seems to be deprecated.

So is there a way to solve my problem ?


The rest of the code:

object ArrayEval {

  import scala.language.experimental.macros

  def eval( a: ArrayOps.ArrayTree ): Array[Double] = macro Macros.evalArrayTree

}

object ArrayOps {

  sealed trait ArrayTree {
    def +( that: ArrayTree ) = Plus( this, that )
  }

  implicit class Ary( val ary: Array[Double] ) extends ArrayTree
  case class Plus( left: ArrayTree, right: ArrayTree ) extends ArrayTree

}

Upvotes: 3

Views: 1665

Answers (2)

Eugene Burmako
Eugene Burmako

Reputation: 13048

The docs for c.eval indeed tell to use c.resetAllAttrs, however this function has a number of known issues that sometimes make it to irreparably corrupt the tree it processes (that's why we're planning to remove it in Scala 2.11 - I just submitted a pull request that does that: https://github.com/scala/scala/pull/3485).

What you could try instead is c.resetLocalAttrs, which has smaller potential for tree corruption. Unfortunately it's still a bit broken. We plan to fix it (https://groups.google.com/forum/#!topic/scala-internals/TtCTPlj_qcQ), however in Scala 2.10.x and 2.11.0 there's going to be no way to make c.eval work reliably.

Upvotes: 3

KChaloux
KChaloux

Reputation: 4038

Well, I figured out what they meant by using resetAllAttrs. My example is simplified for an Int input, but I was able to replicate and fix the error you described by doing the following:

import scala.language.experimental.macros
import scala.reflect.runtime.universe._
import scala.reflect.macros.BlackboxContext

def _evalMacro(c: BlackboxContext)(a: c.Expr[Int]) = {
  import c.universe._

  val treeReset = c.resetAllAttrs(a.tree) // Reset the symbols in the tree for 'a'
  val newExpr = c.Expr(treeReset)         // Construct a new expression for the updated tree

  println(c.eval(newExpr)) // Perform evaluation on the newly constructed expression
  ...                      // Do what you do
}

def evalMacro(a: Int) = macro _evalMacro

I'm going to make a guess that you're fine for using resetAllAttrs, at least until some future versions of Scala come out. 2.11 doesn't even give a deprecation warning for its use.

Note: I'm using Scala 2.11. I believe this should be identical in 2.10, except you'll be using Context instead of BlackboxContext.

Upvotes: 1

Related Questions