问题
I'm working with a scala compiler plugin which replaces function calls like f(x)
with a block
{
val newvalue = f(x)
newvalue
}
This block has the same value as f(x)
.
The compiler plugin works right after compiler phase "typer" and do the transformation above by replacing the AST of f(x) with the AST of the block.
Doing this after compiler phase "typer", the newly created block is a raw AST, without symbol and type, as well as its child ASTs. I need to call localTyper to give the raw ASTs symbols and types.
val newVal = (ValDef(Modifiers(0), newTermName("newvalue"), TypeTree(a.tpe), a))
val newIdent = (Ident(newTermName("newvalue")))
localTyper.atOwner(parSymbol).typed(Block(List(newVal), newIdent))
Note: a
is the AST of function call "f(x)
", newVal
is the AST of "val newvalue = f(x)
". and newIdent
is the reference to "newvalue
" in the block. parSymbol
is the symbol of the owner of old AST "f(x)
", or a
.
The code above works well in most cases. However, when there are some definitions of values (no matter explicit or implicit) in the argument applied to function "f", the compiler issues error. For example, I want to transform:
object hello {
def f(x : Unit) = {}
def g = f {
try {
}
catch {
case t : InterruptedException => t
}
}
}
Note that in the definition of function g, f takes a block
{
try {
}
catch {
case t : InterruptedException => t
}
}
as its parameter, and t
is a newly defined value (implicit) in this parameter.
In this case, I get an error in the compiler phase "icode":
error: scala.reflect.internal.FatalError:
symbol value t#16922 does not exist in hello.g, which contains locals value newvalue#16940.
Method code: def g(): scala.Unit = {
val newvalue: scala#27.runtime#2827.BoxedUnit#4159 = {
hello.this.f({
{
hello.this.liftedTree1$1(t);
scala.runtime.BoxedUnit.UNIT
}
});
scala.runtime.BoxedUnit.UNIT
};
()
}
while compiling: testSynthetic.scala
during phase: icode
library version: version 2.11.7
compiler version: version 2.11.7
reconstructed args: -Xplugin:output.jar -uniqid
last tree to typer: Ident(liftedTree1)
tree position: line 6 of testSynthetic.scala
tree tpe: ()Unit#2648
symbol: (final private[this]) method liftedTree1$1#16976 in object hello#8325
symbol definition: final private[this] def liftedTree1$1#16976(t$1#27328: InterruptedException#151): Unit#2648 (a MethodSymbol)
symbol package: <empty>
symbol owners: method liftedTree1$1#16976 -> object hello#8325
call site: object hello#8325 in package <empty>#4
In phase "icode" the ideal result of transformation should look like this:
def g(): Unit = {
val newvalue: Unit = hello.this.f({
{
hello.this.liftedTree1$1();
scala.runtime.BoxedUnit.UNIT
}
});
newvalue
};
However, in the error message:
def g(): scala.Unit = {
val newvalue: scala#27.runtime#2827.BoxedUnit#4159 = {
hello.this.f({
{
hello.this.liftedTree1$1(t);
scala.runtime.BoxedUnit.UNIT
}
});
scala.runtime.BoxedUnit.UNIT
};
()
}
It seems that the t
should disappear by phase "icode", but because of the transformation, it is somehow reserved but the definition of t
is no longer available. I guess how to set symbols for the raw ASTs are the critical thing here.
来源:https://stackoverflow.com/questions/32389716/how-to-set-the-symbol-for-a-raw-ast-when-writing-a-scala-compiler-plugin