dar*_*ack 7 scala scalac scala-compiler scala-quasiquotes
I have a custom class, A, and I have defined some operations within the class as follows:
def +(that: A) = ...
def -(that: A) = ...
def *(that: A) = ...
def +(that: Double) = ...
def -(that: Double) = ...
def *(that: Double) = ...
Run Code Online (Sandbox Code Playgroud)
In order to have something like 2.0 + x make sense when x is of type A, I have defined the following implicit class:
object A {
implicit class Ops (lhs: Double) {
def +(rhs: A) = ...
def -(rhs: A) = ...
def *(rhs: A) = ...
}
}
Run Code Online (Sandbox Code Playgroud)
This all works fine normally. Now I introduce a compiler plugin with a TypingTransformer that performs some optimizations. Specifically, let's say I have a ValDef:
val x = y + a * z
Run Code Online (Sandbox Code Playgroud)
where x, y, and z are of type A, and a is a Double. Normally, this compiles fine. I put it through the optimizer, which uses quasiquotes to change y + a * z into something else. BUT in this particular example, the expression is unchanged (there are no optimizations to perform). Suddenly, the compiler no longer does an implicit conversion for a * z.
To summarize, I have a compiler plugin that takes an expression that would normally have implicit conversions applied to it. It creates a new expression via quasiquotes, which syntactically appears the same as the old expression. But for this new expression, the compiler fails to perform implicit conversion.
So my question — how does the compiler determine that an implicit conversion must take place? Is there a specific flag or something that needs to be set in the AST that quasiquotes are failing to set?
UPDATE
The plugin phase looks something like this:
override def transform(tree: Tree) = tree match {
case ClassDef(classmods, classname, classtparams, impl) if classname.toString == "Module" => {
var implStatements: List[Tree] = List()
for (node <- impl.body) node match {
case DefDef(mods, name, tparams, vparamss, tpt, body) if name.toString == "loop" => {
var statements: List[Tree] = List()
for (statement <- body.children.dropRight(1)) statement match {
case Assign(opd, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++ List(Assign(opd, optimizedRHS))
}
case ValDef(mods, opd, tpt, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++
List(ValDef(mods, opd, tpt, optimizedRHS))
}
case Apply(Select(src1, op), List(src2)) if op.toString == "push" => {
val optimizedSrc2 = optimizeStatement(src2)
statements = statements ++
List(Apply(Select(src1, op), List(optimizedSrc2)))
}
case _ => statements = statements ++ List(statement)
}
val newBody = Block(statements, body.children.last)
implStatements = implStatements ++
List(DefDef(mods, name, tparams, vparamss, tpt, newBody))
}
case _ => implStatements = implStatements ++ List(node)
}
val newImpl = Template(impl.parents, impl.self, implStatements)
ClassDef(classmods, classname, classtparams, newImpl)
}
case _ => super.transform(tree)
}
def optimizeStatement(tree: Tree): Tree = {
// some logic that transforms
// 1.0 * x + 2.0 * (x + y)
// into
// 3.0 * x + 2.0 * y
// (i.e. distribute multiplication & collect like terms)
//
// returned trees are always newly created
// returned trees are create w/ quasiquotes
// something like
// 1.0 * x + 2.0 * y
// will return
// 1.0 * x + 2.0 * y
// (i.e. syntactically unchanged)
}
Run Code Online (Sandbox Code Playgroud)
UPDATE 2
Please refer to this GitHub repo for a minimum working example: https://github.com/darsnack/compiler-plugin-demo
The issue is that a * z turns into a.<$times: error>(z) after I optimize the statement.
pos该问题与树木相关领域有关。即使一切都发生在 , 之前namer,并且带有和不带有编译器插件的树在语法上是相同的,但由于编译器源代码中的这一讨厌的行,编译器将无法推断隐式转换:
val retry = typeErrors.forall(_.errPos != null) && (errorInResult(fun) || errorInResult(tree) || args.exists(errorInResult))
Run Code Online (Sandbox Code Playgroud)
(感谢hrhino发现了这一点)。
解决方案是在创建新树时始终使用treeCopy,以便复制所有内部标志/字段:
case Assign(opd, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++ List(treeCopy.Assign(statement, opd, optimizedRHS))
}
Run Code Online (Sandbox Code Playgroud)
当使用准引号生成树时,请记住设置位置:
var optimizedNode = atPos(statement.pos.focus)(q"$optimizedSrc1.$newOp")
Run Code Online (Sandbox Code Playgroud)
我使用固定解决方案更新了我的 MWP Github 存储库:https://github.com/darsnack/compiler-plugin-demo
| 归档时间: |
|
| 查看次数: |
182 次 |
| 最近记录: |