3

I tried to implement the Scala macro annotations example as described in the documentation. I managed to compile the macro annotations before the actual project which uses them, i.e., the @compileTimeOnly("enable macro paradise to expand macro annotations") does not get triggered meaning that the macro annotation is compiled before its usage. So far so good.

However, when I annotate certain values in my actual project as follows:

@identity val foo: Double = 1.1
@identity val bar: String = "bar"

then I expect the following print to happen when running the main project (by the macro annotation example linked before):

(<empty>,List(val foo: Double = 1.1))
(<empty>,List(val bar: String = "bar"))

This is where I get confused, the print does not happen when I run the main project. It does however, appear for a split second when compiling the main project as a warning?

(I'm using the IntelliJ IDEA and Scala 2.12.8)

Maarten
  • 207
  • 2
  • 13

1 Answers1

3

I managed to compile the macro annotations before the actual project which uses them, i.e., the @compileTimeOnly("enable macro paradise to expand macro annotations") does not get triggered meaning that the macro annotation is compiled before its usage

No, @compileTimeOnly being triggered would mean the annotation is present after code using it is compiled. So its not being triggered means the macro has been executed during the compilation. And since println is in the body of the macro, not in the transformed code, that's when you see the output.

If you want printing to happen when running the project, you need to modify the return value which contains the transformed code, i.e. the last two lines in the example:

val outputs = expandees
c.Expr[Any](Block(outputs, Literal(Constant(()))))

either using quasiquotes or directly manipulating ASTs.

Untested, but using quasiquotes something approximately like this should work

object identityMacro {
  def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
    import c.universe._
    val inputs = annottees.map(_.tree).toList
    val (annottee, expandees) = inputs match {
      case (param: ValDef) :: (rest @ (_ :: _)) => (param, rest)
      case (param: TypeDef) :: (rest @ (_ :: _)) => (param, rest)
      case _ => (EmptyTree, inputs)
    }
    val stringToPrint = (annottee, expandees).toString
    c.Expr[Any](q"""
    println($stringToPrint)
    $expandees
    ()
    """)
  }
}
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Thank you, this solves my problem! So if I understand correctly it is just the AST that is returned (in your case constructed using quasiquotes) by the macro that gets expanded in the code using that macro? – Maarten Apr 03 '19 at 11:32
  • 1
    Yes, that's correct. `identityMacro.impl` is just a normal Scala method which happens to manipulate ASTs and get executed during compilation of code using `@identity`. – Alexey Romanov Apr 03 '19 at 11:38