Compilation issue when accessing parameter value in Scala macro

泄露秘密 提交于 2019-12-11 04:53:56

问题


The calling code for this minimal example appears to compile (Eclipse Indigo SR2, Scala v2.10.20), but the project containing the calling code is marked with a red cross (not clear to me how to get further diagnostics for this). No class files are generated. If I replace param.value with some literal e.g. 1, the calling code compiles.

Is this a known problem? Is there a workaround?

def myMacro( param : Int ): Int = macro myMacroImpl( param )

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
    import c.universe._

    c.Expr( Literal( Constant( param.value ) ) )         
}

回答1:


First of all, the syntax of macro definitions doesn't require (or allow) you to specify the parameters to the macro implementation method, so the code you have should never compile, even with a literal in the implementation. See my examples below for the correct syntax.

Next, Expr.value is unfortunately a lie. Think about the case where the argument to the macro method is a variable. The value of the variable isn't (and in the general case couldn't be) known at compile-time, and yet you're trying to make a compile-time literal constant with that value. It's just fundamentally not going to work.

You have a couple of options, which may or may not be applicable depending on what you're trying to do. Suppose we need to add one to the param value and return the result, for example. If we wanted the addition to happen at compile-time, we'd have to throw a (compile-time) exception when we don't get a compile-time literal:

def myMacro(param: Int): Int = macro myMacroImpl

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._

  val p = param.tree match {
    case Literal(Constant(p: Int)) => p
    case _ => c.abort(c.enclosingPosition, "param must be a literal value!")
  }

  c.literal(p + 1) // Or, equivalently: c.Expr(Literal(Constant(p + 1))
}

Another option would be to build a tree around the param expression. For example, the following also returns the successor to param, but the addition is performed at runtime, and the method can handle non-literal arguments:

def myMacro(param: Int): Int = macro myMacroImpl

def myMacroImpl(c: Context)(param: c.Expr[Int]): c.Expr[Int] = {
  import c.universe._

  c.Expr(
    Apply(Select(param.tree, newTermName("$plus")), c.literal(1).tree :: Nil)
  )
}

We're just not going to get both compile-time addition and an arbitrary param expression, though.



来源:https://stackoverflow.com/questions/18519565/compilation-issue-when-accessing-parameter-value-in-scala-macro

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!