简体   繁体   中英

Scala macro does not compile when code is crafted without reify

I've been working on complex compile-time reflection and have come across the need to craft Scala code manually, using the AST. While experimenting I noticed a weird compilation error which doesn't really make sense to me, so I tried reproducing it on a test project.

I use Scala 2.10.4 .

Here's the code:

Macro.scala:

object Macro {
  def reifyTestImpl(c: Context): c.Expr[OffsetDateTime] = {
    import c.universe._
    val expression = reify(OffsetDateTime.now())
    c.echo(c.enclosingPosition, "With reify: " + show(expression.tree))
    c.echo(c.enclosingPosition, "With reify (raw): " + showRaw(expression.tree))
    expression
  }

  def manualAstTestImpl(c: Context): c.Expr[OffsetDateTime] = {
    import c.universe._
    val odtSymbol = typeOf[OffsetDateTime].typeSymbol
    val now = newTermName("now")
    val expression = c.Expr(
      Apply(
        Select(Ident(odtSymbol), now),
        List()
      )
    )
    c.echo(c.enclosingPosition, "Manual:     " + show(expression.tree))
    c.echo(c.enclosingPosition, "Manual (raw):     " + showRaw(expression.tree))
    expression
  }

  def reifyTest = macro reifyTestImpl
  def manualAstTest = macro manualAstTestImpl
}

Tester.scala:

object Tester {
  def main(args: Array[String]): Unit = {
    println(Macro.reifyTest)
    println(Macro.manualAstTest)
  }
}

The output from c.echo is:

With reify: OffsetDateTime.now()
With reify (raw): Apply(Select(Ident(java.time.OffsetDateTime), newTermName("now")), List())
Manual:     OffsetDateTime.now()
Manual (raw):     Apply(Select(Ident(java.time.OffsetDateTime), newTermName("now")), List())

The compilation error I get is value now is not a member of java.time.OffsetDateTime on the call to Macro.manualAstTest .
As the output from the echoes suggests, the two expressions are identical - yet one works (the expression from reify ) and the other does not (the expression crafted with apply-select).

What could be the difference between the two?

Managed to find the culprit.
Apparently typeOf[OffsetDateTime].typeSymbol returns the symbol as would've returned from a Scala class, that is, without its static members.

Adding .companionSymbol seems to return the symbol as would've returned from a Scala object, that is, with only the static members (as the name implies...)

So the following change makes it work:

val odtSymbol = typeOf[OffsetDateTime].typeSymbol.companionSymbol

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM