Scala 3编译器插件生成预期的代码,但在运行时失败



我正在尝试开始为scala 3编写编译器插件。在这个阶段,它主要基于https://github.com/liufengyun/scala3-plugin-example/blob/master/plugin/src/main/scala/Phases.scala(以及随附的youtube视频解释它是如何工作的)。

到目前为止,这是一个有趣的过程,我对编译器的某些方面有了一点感觉。

作为第一步,我只是简单地尝试将方法体包装到一个块中,打印返回的对象,然后返回对象。

这与原始插件的主要不同之处在于,每个方法都添加了一个副作用的方法调用——这也分配了一个局部变量,(我认为这可能是问题的原因),并将方法体移动到一个块中。

我已经在这里创建了一个尽可能少的工作示例:https://github.com/robmwalsh/scala3-plugin-example

插件编译得很好,似乎像预期的那样作为编译的一部分运行,然后在运行时爆炸。我不完全确定这是我做错了什么(不是不可能)还是编译器中的错误(不太可能,但很有可能!)。

有谁能解释一下为什么这不起作用吗?我不知道在创建一个新的Symbol时应该设置什么标志,所以这是一种可能性,但是有很多东西似乎是可行的,所以我就用它了。

我在这里(有趣的部分):

...
override def prepareForUnit(tree: Tree)(using ctx: Context): Context =
//find the printLn method
val predef = requiredModule("scala.Predef")
printlnSym = predef.requiredMethod("println", List(defn.AnyType))
ctx
override def transformDefDef(tree: DefDef)(using ctx: Context): Tree =
val sym = tree.symbol
// ignore abstract and synthetic methods
if tree.rhs.isEmpty|| sym.isOneOf(Synthetic | Deferred | Private | Accessor)
then return tree
try {
println("nnnn")
println("========================== tree ==========================")
println(tree.show)
// val body = {tree.rhs}
val body = ValDef(
newSymbol(
tree.symbol, termName("body"), tree.symbol.flags, tree.rhs.tpe),
Block(Nil, tree.rhs)
)

// println(body)
val bodyRef  = ref(body.symbol)
val printRes = ref(printlnSym).appliedTo(bodyRef)
// shove it all together in a block
val rhs1 = tpd.Block(body :: printRes :: Nil, bodyRef)
//replace RHS with new
val newDefDef = cpy.DefDef(tree)(rhs = rhs1)
println("====================== transformed ======================")
println(newDefDef.show)
newDefDef
} catch {
case e =>
println("====================== error ===========================")
println(e)
println(e.printStackTrace)
tree
}
...

编译器插件测试程序

object Test extends App:
def foo: String = "forty two"
def bar(x: String): Int = x.length
def baz(x: String, y: Int): String = x + y
baz(foo, bar(foo))

输出期间编译使用插件(正是我想要的!这时我非常兴奋)

========================== tree ==========================
def foo: String = "forty two"
====================== transformed ======================
def foo: String = 
{
val body: ("forty two" : String) = 
{
"forty two"
}
println(body)
body
}

========================== tree ==========================
def bar(x: String): Int = x.length()
====================== transformed ======================
def bar(x: String): Int = 
{
val body: Int = 
{
x.length()
}
println(body)
body
}

========================== tree ==========================
def baz(x: String, y: Int): String = x.+(y)
====================== transformed ======================
def baz(x: String, y: Int): String = 
{
val body: String = 
{
x.+(y)
}
println(body)
body
}

运行时的输出:'((这取决于它运行的代码,但始终是相同的主题)

Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
testing/Test$.body$2()I @0: aload_1
Reason:
Type top (current frame, locals[1]) is not assignable to reference type
Current Frame:
bci: @0
flags: { }
locals: { 'testing/Test$' }
stack: { }
Bytecode:
0000000: 2bb6 007d ac
at testing.Test.main(Example.scala)

编辑:我使用scala 3.1.2

我在创建新符号时使用了现有的标志。相反,我需要使用Local标志,我认为这是有意义的。

最新更新