What is wrong with this def macro?

被刻印的时光 ゝ 提交于 2020-01-04 13:04:08

问题


Disclaimer: this code is not of practical use and serves education purposes only.

tl;dr: most of it is just a result of me trying to debug the issue, so only first 3 snippets matter.

Here is the macro definition:

def tx[T](ds: GraphDatabaseService)(block: => T): Option[T] = 
  macro txmacros.blockTxImpl[T]

Here is the implementation:

def blockTxImpl[T: c.WeakTypeTag](c: whitebox.Context)(ds: c.Tree)(block: c.Tree):
    c.Tree = {
  import c.universe._

  q"""
    val tx = $ds.beginTx()
    val newRetVal = try {
      val retVal = {
        $block
      }
      tx.success()
      Option(retVal)
    } catch {
      case _ : Throwable =>
        tx.failure()
        None
    } finally {
      tx.close()
    }
    newRetVal
  """
}

And here is how it's called:

val nodePropK5 = tx(db) {
  // simplified for brevity
  val node = db.find(label, "key", 100).iterator().next()
  node.getProperty("k5", 300)
}

nodePropK5 should be (Some(200))

The whole project can be found to https://github.com/cdshines/txMacro/ (ready to be built an run).

Such an invocation fails with the following message:

[error] symbol value node does not exist in MacroTest$$anonfun$3.apply$mcV$sp
[trace] Stack trace suppressed: run last core/test:compile for the full output.
[error] (core/test:compile) scala.reflect.internal.FatalError: symbol value node does not exist in MacroTest$$anonfun$3.apply$mcV$sp

If I, however, change the problematic code to

val nodePropK5 = tx(db) {
  db.findNodesByLabelAndProperty(label, "k4", 100).iterator().next().getProperty("k5", 300)
}

the return value is Some(300) as expected. Adding lines that don't declare new variables (or use node) doesn't break the behavior, while

val nodePropK5 = tx(db) {
  db.findNodesByLabelAndProperty(label, "k4", 100).iterator().next().getProperty("k5", 300)
  val x = 5
  x
}

results in the same message.

Another peculiar thing: if I print the failing block during macro expansion, i get the following code:

{
  val tx = MacroTest.this.db.beginTx();
  val newRetVal = try {
    val retVal = {
      val node: org.neo4j.graphdb.Node = MacroTest.this.db.findNodesByLabelAndProperty(MacroTest.this.label, "k4", 100).iterator().next();
      node.getProperty("k5", 300)
    };
    tx.success();
    Option(retVal)
  } catch {
    case (_: Throwable) => {
      tx.failure();
      None
    }
  } finally tx.close();
  newRetVal
}

Which, being manually substituted, works just fine.

What am I missing here? Am I free to assume that this is a compiler bug?


回答1:


When you see this kind of error the first thing to try is un-typechecking code that you're "reusing" in your macro output. In this case replacing the following:

val retVal = {
  $block
}

With:

val retVal = {
  ${ c.untypecheck(block) }
}

Should do the trick.

Note that in 2.10 the equivalent of untypecheck was resetAllAttrs / resetLocalAttrs. If you search for those names you'll find a lot of discussion of the details of the problem you're seeing.



来源:https://stackoverflow.com/questions/25449684/what-is-wrong-with-this-def-macro

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