gpt4 book ai didi

scala - Scala 编译器如何进行隐式转换?

转载 作者:行者123 更新时间:2023-12-04 12:26:48 25 4
gpt4 key购买 nike

我有一个自定义类,A ,并且我在类中定义了一些操作,如下所示:

def +(that: A) = ...
def -(that: A) = ...
def *(that: A) = ...

def +(that: Double) = ...
def -(that: Double) = ...
def *(that: Double) = ...

为了有类似 2.0 + x 的东西有意义的时候 x类型为 A ,我定义了以下隐式类:

object A {
implicit class Ops (lhs: Double) {
def +(rhs: A) = ...
def -(rhs: A) = ...
def *(rhs: A) = ...
}
}

这一切正常。现在我介绍一个带有 TypingTransformer 的编译器插件执行一些优化。具体来说,假设我有一个 ValDef :

val x = y + a * z

哪里 x , y , 和 z属于 A 类型, 和 aDouble .通常,这可以很好地编译。我把它通过优化器,它使用 quasiquotes 来改变 y + a * z变成别的东西。 但是 在这个特定的例子中,表达式没有改变(没有要执行的优化)。突然间,编译器不再对 a * z 进行隐式转换。 .

总而言之,我有一个编译器插件,它采用一个通常会应用隐式转换的表达式。它通过 quasiquotes 创建一个新表达式,它在语法上与旧表达式相同。但是对于这个新表达式,编译器无法执行隐式转换。

所以我的问题 - 编译器如何确定必须进行隐式转换?是否有特定的标志或需要在 AST 中设置的 quasiquotes 未能设置的内容?

更新

插件阶段看起来像这样:

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)
}

更新 2

请参阅此 GitHub 存储库以获取最低工作示例: https://github.com/darsnack/compiler-plugin-demo

问题是 a * z变成 a.<$times: error>(z)在我优化语句之后。

最佳答案

该问题与pos有关与树相关的字段。即使一切都发生在 namer 之前,并且带有和不带有编译器插件的树在语法上是相同的,由于编译器源代码中的这个讨厌的行,编译器将无法推断隐式转换:

val retry = typeErrors.forall(_.errPos != null) && (errorInResult(fun) || errorInResult(tree) || args.exists(errorInResult))

(归功于 hrhino 找到了这个)。

解决方案是始终使用 treeCopy创建新树以复制所有内部标志/字段时:

case Assign(opd, rhs) => {
val optimizedRHS = optimizeStatement(rhs)
statements = statements ++ List(treeCopy.Assign(statement, opd, optimizedRHS))
}

并且在使用 quasiquotes 生成树时,请记住设置位置:

var optimizedNode = atPos(statement.pos.focus)(q"$optimizedSrc1.$newOp")

我使用固定解决方案更新了我的 MWP Github 存储库: https://github.com/darsnack/compiler-plugin-demo

关于scala - Scala 编译器如何进行隐式转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55680855/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com