需要宏取消类型检查 [英] Macro untypecheck required

查看:51
本文介绍了需要宏取消类型检查的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用Macros生成一些代码的开源项目中遇到了问题.如果使用c.untypecheck,一切都可以正常工作,但理想情况下,我不想这样做.

I'm running into problems in my open-source project using Macros to generate some code. Everything works fine if I use c.untypecheck, but ideally I'd prefer not to have to do that.

这是相关的代码: https://github.com/outr/reactify/blob/master/shared/src/main/scala/com/outr/reactify/Macros.scala#L46

如果删除c.untypecheck,则会出现以下编译时错误:

If I remove the c.untypecheck I get the following compile-time error:

[error] (reactifyJVM/test:compileIncremental) java.lang.AssertionError: assertion failed: 
[error]   transformCaseApply: name = previousVal tree = previousVal / class scala.reflect.internal.Trees$Ident
[error]      while compiling: /home/mhicks/projects/open-source/reactify/shared/src/test/scala/specs/BasicSpec.scala
[error]         during phase: refchecks
[error]      library version: version 2.12.1
[error]     compiler version: version 2.12.1
[error]   reconstructed args: -classpath /home/mhicks/projects/open-source/reactify/jvm/target/scala-2.12/test-classes:/home/mhicks/projects/open-source/reactify/jvm/target/scala-2.12/classes:/home/mhicks/.ivy2/cache/org.scala-lang/scala-reflect/jars/scala-reflect-2.12.1.jar:/home/mhicks/.ivy2/cache/org.scalatest/scalatest_2.12/bundles/scalatest_2.12-3.0.1.jar:/home/mhicks/.ivy2/cache/org.scalactic/scalactic_2.12/bundles/scalactic_2.12-3.0.1.jar:/home/mhicks/.ivy2/cache/org.scala-lang.modules/scala-xml_2.12/bundles/scala-xml_2.12-1.0.5.jar:/home/mhicks/.ivy2/cache/org.scala-lang.modules/scala-parser-combinators_2.12/bundles/scala-parser-combinators_2.12-1.0.4.jar -bootclasspath /usr/java/jdk1.8.0_92/jre/lib/resources.jar:/usr/java/jdk1.8.0_92/jre/lib/rt.jar:/usr/java/jdk1.8.0_92/jre/lib/sunrsasign.jar:/usr/java/jdk1.8.0_92/jre/lib/jsse.jar:/usr/java/jdk1.8.0_92/jre/lib/jce.jar:/usr/java/jdk1.8.0_92/jre/lib/charsets.jar:/usr/java/jdk1.8.0_92/jre/lib/jfr.jar:/usr/java/jdk1.8.0_92/jre/classes:/home/mhicks/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.1.jar
[error] 
[error]   last tree to typer: TypeTree(class Position)
[error]        tree position: line 148 of /home/mhicks/projects/open-source/reactify/shared/src/test/scala/specs/BasicSpec.scala
[error]             tree tpe: org.scalactic.source.Position
[error]               symbol: case class Position in package source
[error]    symbol definition: case class Position extends Product with Serializable (a ClassSymbol)
[error]       symbol package: org.scalactic.source
[error]        symbol owners: class Position
[error]            call site: <$anon: com.outr.reactify.ChangeListener[Int]> in package specs
[error] 
[error] == Source file context for tree position ==
[error] 
[error]    145       current should be(15)
[error]    146     }
[error]    147     "observe a complex change" in {
[error]    148       val v1 = Var(5)
[error]    149       val v2 = Var(10)
[error]    150       val v3 = Var(v1 + v2)
[error]    151       var changed = 0
[error] Total time: 1 s, completed Jan 31, 2017 4:43:03 PM

如果我将其重新添加,则所有内容均可编译并正常运行.在更复杂的用例中,我在编译时遇到了一些问题Could not find proxy for ...,我认为这可能是原因.

If I add it back everything compiles and works just fine. In more complex use-cases I've been encountering some issues at compile-time Could not find proxy for ... and I think this might be the reason.

任何建议将不胜感激.

推荐答案

您正在将无类型树引入有类型树中.

You're introducing an untyped tree into a typed tree.

对传入的树进行类型检查,然后再次对输出的树(您的宏发出的树)进行类型检查,但打字机不会下降到已经进行类型检查的树中(即,已经为其分配了类型的树).

The incoming tree is typechecked, and then the outgoing tree (that your macro emits) is typechecked again, but the typer does not descend into a tree that is already typechecked (i.e., that has a type already assigned to it).

因为要引入新符号,所以不能仅使用传入上下文来对引用进行类型检查.

Because you're introducing new symbols, you can't just use the incoming context to typecheck your reference.

因此,最简单的解决方案是您到达的结果,以取消对输出树的类型检查.取消对transformed树的类型检查也足够了,以允许typer下降到新的未类型化树.

So, the simplest solution is what you arrived at, to untypecheck the outgoing tree. It's also sufficient to untypecheck the transformed tree, to allow typer to descend to your new, untyped tree.

我不得不通过注释掉代码来减少爆炸测试.不幸的是,尚不清楚哪个源代码行导致了错误.如果您熟悉所涉及的宏,也许会更加明显.

I had to reduce the exploding test by commenting out code. It's unfortunate that it's not immediately obvious what source line causes the error. Maybe it's more obvious if you're familiar with the macro involved.

class Sample {
  def sample(): Unit = {
    val v = Var(5)
    v := v + 5
  }
}

有问题的树,来自-Xprint:typer -Yshow-trees:

            Apply( // def +(x: Int): Int in class Int, tree.tpe=Int
              com.outr.reactify.`package`.state2Value[Int](previousVal)."$plus" // def +(x: Int): Int in class Int, tree.tpe=(x: Int)Int
              5
            )

还值得一提的是,在错误消息中编写带有重构的args"的快速编译脚本会更容易,以消除sbt增量编译,ScalaTest宏和其他奥秘.

Also worth mentioning that it was easier to write a quick compile script with the "reconstructed args" in the error message, to eliminate sbt incremental compilation, ScalaTest macros and other mysteries.

编辑,用于手动设置的API:

Edit, the API for setting by hand:

def setStateChannel(value: c.Tree): c.Tree = {
  val observables = retrieveObservables(c)(value)
  val channel = c.prefix.tree
  val selfReference = observables.exists(_.equalsStructure(channel))

  val untyped =
    q"""
      val previousValue = com.outr.reactify.State.internalFunction($channel)
      val previousVal = com.outr.reactify.Val(previousValue())
    """
  val retyped = c.typecheck(untyped)
  val transformed = if (selfReference) {
    val transformer = new Transformer {
      override def transform(tree: c.universe.Tree): c.universe.Tree = if (tree.equalsStructure(channel)) {
        val t = q"previousVal"
        val Block(_ :: v :: Nil, _) = retyped
        c.internal.setSymbol(t, v.symbol)
        c.internal.setType(t, v.tpe)
      } else {
        super.transform(tree)
      }
    }
    transformer.transform(value)
  } else {
    value
  }
  val res = q"$channel.update(List(..$observables), $transformed)"
  q"$retyped ; $res"
}

这篇关于需要宏取消类型检查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆