Scala宏:typed(又名typechecked)和无类型树之间有什么区别

时间:2014-01-05 17:10:24

标签: scala typechecking scala-macros

我开始使用scala宏,它们非常棒,但我遇到了类型(也称为类型检查)和无类型Tree之间的区别。

例如,出于某种原因,您无法使用类型检查的树调用c.eval。我在scala宏文档中找不到关于这个'typechecked'的文档(我知道他们仍然在努力,这可能是某些事情需要在某天添加的。)

Tree进行类型检查意味着什么?为什么它们如此不同以至于显然c.eval无法处理类型为Tree的问题(逆向对我来说更有意义)。

我想这可能是编译器101,但我没有采取这个过程:( 任何解释或指向文章/文件的指针将不胜感激!

1 个答案:

答案 0 :(得分:33)

理论部分

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

非常粗略地说,scalac的前端包含一个解析器和一个typer,它们都可以处理树木并生成树木。然而,这些树的属性是完全不同的,这是因为解析器生成的树是未归因的(将symbol字段设置为NoSymbol并将其tpe字段设置为{{1而由typer产生的树是归因的。

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

好吧,但是为什么人们想要删除(或者用scalac说法,重置,如tperesetLocalAttrs)属性的类型树?嗯,这种必要性源于另一个实现细节 - 符号及其所有者链。就在几天前,我已经在scala-internals上写了一些关于它的细节:https://groups.google.com/d/msg/scala-internals/rIyJ4yHdPDU/qS-7DreEbCwJ,但简而言之,你不能在一些词汇上下文中检查树,然后在不同的词汇上下文中使用它(这是resetAllAttrs)基本上需要的。

所以,总结一下scalac树管理的最新技术:

  1. 无类型树(也称为解析树或未归属树)在观察上与类型树(也称为typer tree,typechecked树或属性树)不同。
  2. 这两种树的味道有两个主要区别:a)打字的树有由typechecker设置的符号和类型,b)打字的树有不同的形状。
  3. 通常,如果某个编译器API采用树,那么无类型树和类型树都可以。但是在某些情况下(我在上面概述了其中一个),只有非类型化或仅键入的树是合适的。
  4. 可以通过调用c.eval(编译时反射)或Context.typecheck(运行时反射)从非类型树到类型化树,但是从类型树返回到无类型树由于https://issues.scala-lang.org/browse/SI-5464ToolBox.typecheckresetLocalAttrs目前不可靠。
  5. 所以,正如你所看到的,我们的树很反复无常,这给Scala中的元编程带来了很大的复杂性。

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

    实用部分

    在通过详细阐述所有细节并暗示无效的神秘案例彻底吓唬你之后,我想要注意的是,在使用宏时通常不需要知道这种东西。通常,无类型树(手动构造的AST,quasiquotes)和类型树(宏参数)都可以正常工作。

    如果scalac想要一种特定的树味,它会告诉你像resetAllAttrs或有时会崩溃(RefChecks,LambdaLift和GenICode崩溃是树木在宏观扩张期间混淆的巨大指标 - 在这些情况下,使用https://groups.google.com/forum/#!msg/scala-internals/rIyJ4yHdPDU/qS-7DreEbCwJ中所述的c.eval。解决这个问题是我的首要任务,我现在正在努力解决这个问题。可能会发生这样的情况,修复程序将使其成为2.11.0,这个答案很快就会过时:)