问题

我开始使用scala宏,它们很棒,但是我遇到了类型化(aka typechecked)和未类型化 Tree 之间的差异。

例如,由于某种原因,您不能使用经过类型检查的树来调用 c.eval 。我无法在scala宏文档中找到有关此“经过类型检查”的文档(我知道他们仍在对此进行操作,这可能有一天需要添加)。

Tree 被类型检查是什么意思?为什么它们如此不同,以至于c.eval显然不能处理typechecked Tree s(对我而言,反比更有意义)。

我想这可能是编译器101,但我没有上这门课:(任何对文章/教义的解释或指点,不胜感激!

  最佳答案

理论部分

这是scalac的结构特性,一旦我们在2.10中的编译时/运行时反射中公开了内部编译器数据结构,就开始泄漏到公共API中。

粗略地说,scalac的前端包含一个解析器和一个输入器,两者均与树配合使用并产生树。但是,这些树的属性完全不同,这是因为解析器生成的树是未归因的(将 symbol 字段设置为 NoSymbol ,将 tpe 字段设置为 null ),而打字员归于。

现在您可能想知道这会有什么不同,因为它只是 symbol tpe 吧?然而,在scalac中,不仅如此。为了完成其工作,typer更改了它正在处理的AST的结构,破坏了一些原始树并生成了一些合成树。不幸的是,有时这些转换是不可逆的,这意味着如果一个类型检查一棵树,然后擦除所有分配的属性,则结果树不再有意义(https://issues.scala-lang.org/browse/SI-5464)。

好的,但是为什么要删除(或用 resetLocalAttrs resetAllAttrs 中的重置标语)类型检查树的属性?嗯,这种必要性源于另一个实现细节-符号及其所有者链。就在几天前,我已经在scala-internals上写了关于它的一些详细信息:https://groups.google.com/d/msg/scala-internals/rIyJ4yHdPDU/qS-7DreEbCwJ,但是总的来说,您不能在某些词法上下文中对树进行类型检查,然后在其中使用它不同的词法上下文(这实际上是 c.eval 所需要的)。

因此,总结一下scalac树管理的最新技术水平:

  1. 在观察上,未分类的树(也称为解析器树或未归类的树)与已分类的树(也称为分类器树,经类型检查的树或归因树)不同
  2. 这两种树的风格之间有两个主要区别:a)类型树具有由类型检查器设置的符号和类型,b)类型树的形状略有不同。
  3. 通常,如果某些编译器API接受一棵树,则无类型树和有类型树都将起作用。但是,在某些情况下(我在上面概述了其中的一种),仅适合无类型树或仅具有类型树。
  4. 可以通过调用 Context.typecheck (编译时反射)或 ToolBox.typecheck (运行时反射)从无类型树变为有类型树,但是可以通过 resetLocalAttrs resetAllAttrs 从无类型树返回无类型树。由于https://issues.scala-lang.org/browse/SI-5464当前是不可靠的。

因此,正如您所看到的,我们的树是反复无常的,这在Scala的元编程中带来了很多复杂性。

但是,好消息是,这种复杂性不是由编译器101产生的一些基本的好理由所决定的。所有复杂性都是偶然的,我们计划逐步将其逐出,直到全部消失。 https://groups.google.com/forum/#!topic / scala-internals / TtCTPlj_qcQ(也在几天前发布)是朝此方向迈出的第一步。请继续关注可能于今年推出的其他商品!

实践部分

在详细阐述了所有细节并暗示了什么都无济于事的神秘情况之后,我彻底地吓了一跳,之后我想指出的是,通常在使用宏时不需要了解这类信息。通常,无类型树(手动构造的AST,准引用)和有类型树(宏参数)都可以正常工作。

在某些情况下,当scalac想要一种特定的树风味时,它会告诉您 c.eval 还是有时在您面前崩溃(RefChecks,LambdaLift和GenICode崩溃是在宏扩展过程中树木混杂的巨大指标-在这种情况下使用(如https://groups.google.com/forum/#中所述 resetLocalAttrs !msg / scala-internals / rIyJ4yHdPDU / qS-7DreEbCwJ)。解决此问题是我的头等大事,我现在正在研究中。可能发生的情况是该修复程序将其纳入2.11.0,此答案很快就会过时了:)

  相同标签的其他问题

scalatypecheckingscala-macros