I'm currently playing around a bit with macros and maybe this is a bad idea anyway, but here's my problem:
I have the following macro:
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) = macro usingImpl[A, B]
def usingImpl[A <: { def close(): Unit }, B](c: Context)(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = {
import c.universe._
f.tree match {
case Function(params, body) =>
//val ValDef(modifiers, name, tpt, _) = params.head
c.Expr[B](
Block(
List(
//ValDef(modifiers, name, tpt, resource.tree)
ValDef(params.head.symbol, resource.tree)
),
body
)
)
case _: Select =>
reify {
val res = resource.splice
try {
f.splice(res)
} finally {
res.close()
}
}
}
}
In case of a Select
, I simply call the function and close the resource, works fine. But in case of a Function
, I'd like to assign the param value to the resource and call the body. When I'm using the deprecated creator of ValDef
, that takes a Symbol
and a Tree
, everything works fine. If I'm using the out-commented 4-args creator, I get a compiler error, stating that the value x$1
is not in scope. When I look at the code that both versions produce, it looks exactly the same:
Expr[Int]({
<synthetic> val x$1: Test.Foo = new Test.this.Foo();
x$1.bar.+(23)
})
Is there maybe a way, to simply use params.head
and assign a value? Thanks for any help!
edit :
I call the macro like this:
object Test extends App {
import Macros._
class Foo {
def close() {}
def bar = 3
}
println(using(new Foo)(_.bar + 3))
}
As I said, if I'm using the out-commented version, it gives me a compiler error, that prints the AST and at the end this message: [error] symbol value x$1 does not exist in Test$delayedInit$body.apply
And I'm using 2.10.1.
Ah, now I can reproduce your error. Any time you get the "This entry seems to have slain the compiler." message and see lines like this in the stack trace:
at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
Your next move should be to start wrapping stuff in c.resetAllAttrs
. To be honest the reason I couldn't reproduce your error the first time around is because I had replaced body
in the block with c.resetAllAttrs(body)
after copying and pasting your code, without even thinking about it. It's just a reflex at this point.
See for example this little single abstract method class demo for a similar instance of a place where this trick is necessary.
In your case, if we have this:
import scala.language.experimental.macros
import scala.reflect.macros.Context
object Macros {
def using[A <: { def close(): Unit }, B](resource: A)(f: A => B) =
macro usingImpl[A, B]
def usingImpl[A <: { def close(): Unit }, B](c: Context)
(resource: c.Expr[A])(f: c.Expr[A => B]): c.Expr[B] = {
import c.universe._
val expr = f.tree match {
case Function(ValDef(modifiers, name, tpt, _) :: Nil, body) =>
c.Expr[B](
Block(
ValDef(modifiers, name, tpt, resource.tree) :: Nil,
c.resetAllAttrs(body)
)
)
case _: Select => reify {
val res = resource.splice
try { f.splice(res) } finally { res.close() }
}
}
println(expr)
expr
}
}
We'll see the following when we compile your test code:
Expr[B]({
<synthetic> val x$1: $line3.$read.$iw.$iw.Test.Foo = new Test.this.Foo();
x$1.bar.$plus(3)
})
Exactly as desired.
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.