Question

How can I see the definition of a symbol from within macros?

As a special case, I wonder how to evaluate constant values at compile time using macros. My approach works for "final val" but not for a local "val":

// scalaVersion := "2.11.0"
import scala.reflect.macros.blackbox.Context
import scala.language.experimental.macros

object Const {
  def m_same(c: Context)(i: c.Tree) = {
    import c.universe._
    val Literal(Constant(_)) = i
    i
  }
  def same(i: Any): Any = macro m_same
}

A local value is not recognized as a Literal(Constant(_)):

import org.scalatest.FreeSpec

class ConstSpec extends FreeSpec {
  "Int literal" in {
    assert(Const.same(42) === 42)
  }

  final val iFinal = "mytest"            // is a Literal(Constant(_))
  "final Int value" in {
    assert(Const.same(iFinal) === iFinal)
  }

  "local Int value" in {
    val iLocal = 42                       // is NOT a Literal(Constant(_))
    assert(Const.same(iLocal) === iLocal) // does NOT compile
  }
}
Was it helpful?

Solution

final val has special significance for constants and means inline them.

Your ordinary val looks like:

scala> q"{ val i = 42 ; i }"
res0: reflect.runtime.universe.Tree =
{
  val i = 42;
  i
}

scala> showRaw(res0)
res1: String = Block(List(ValDef(Modifiers(), TermName("i"), TypeTree(), Literal(Constant(42)))), Ident(TermName("i")))

I think Travis Brown's Metaplasm blog, i.e., things you shouldn't try at home with macros, includes examining the surrounding context for a def.

In this case, consulting the enclosing context on a statement basis would discover the def of the i you want, and you could examine its RHS.

Their macro philosophy is to think local and expand local. I just made that up. They also offer macro annotations for less-local things like companions.

But wait, they do offer an eval facility on Context. Is that sufficient for your use case?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top