【问题标题】:When / where type checking occurs in the compilation process编译过程中何时/何地进行类型检查
【发布时间】:2018-11-05 21:33:08
【问题描述】:

在编译过程中(在高层次上)通常发生类型检查(教科书与实践)时,从高层次上想知道。我对编译过程的大致理解是:

  1. 将源代码解析为 AST
  2. 将 AST 转换为中间表示 IR
  3. 优化 IR(即 SSA 表格、注册分配等)
  4. 简化 IR
  5. 生成最终输出代码

想知道类型检查是发生在(1)和(2)之间,(2)和(3)之间,还是在(4)之后,或者它是否在整个过程中发生,或者其他什么。我很想知道面向对象、函数式和逻辑编程的答案(按优先级顺序),但如果我必须选择一个,那么 OO,比如像 Ruby 这样的动态类型语言,或者像静态类型的函数式语言哈斯克尔。

【问题讨论】:

    标签: ruby haskell compiler-construction jit typechecking


    【解决方案1】:

    静态类型检查通常在 AST 上执行,因此它要么发生在 1 和 2 之间,要么作为 2 的一部分(这意味着 IR 生成器在处理 AST 节点时从类型检查器调用函数 - 当然IR 生成器和类型检查器应该仍然存在于不同的模块/文件中)。

    理论上,您可以对 IR 执行类型检查,但这通常会导致至少以下问题之一:

    1. IR 没有包含足够的信息来捕获您想要的所有错误。
    2. IR 没有包含足够的信息来在所有情况下生成最佳错误消息。例如,考虑 IR 表示使用相同指令的数组访问和指针运算。现在您想为使用浮点索引的数组访问产生错误。如果消息是“不允许将浮点值用作指针运算的操作数”,那么当代码不包含任何指针运算时,这会令人困惑。要求用户知道数组访问表示为指针算法以理解错误消息,这对用户来说不是很友好。
    3. 您向 IR 添加大量额外信息以进行类型检查,使 IR 更加复杂和笨拙,但您从中获得的只是能够以与您相同的方式处理 IR AST 会不会获得任何好处。

    通常使用 IR 而不是 AST 意味着您不必处理尽可能多的情况(正是因为 IR 使用相同的指令代表不同的事物)。这是主要的好处。但是,如果您为了能够再次以不同的方式处理这些案例而跳过额外的障碍,那么您不妨一开始就使用 AST。

    因此通常首选对 AST¹ 进行类型检查。 GHC(主要的 Haskell 编译器)执行 AST 的类型检查。

    ¹ 或者至少是非常接近 AST 的东西 - 例如,可能存在 AST 和最终 IR 之间的表示,它在某些方面简化了事情(例如删除扁平嵌套表达式),而不会丢失相关信息进行类型检查。


    动态类型检查发生在运行时。执行这些动态类型检查的代码要么是解释器的一部分(如果有解释器),要么是由代码生成器插入的。

    Ruby 在解释器中执行类型检查。

    【讨论】:

    • 依赖类型语言(例如,Agda、Coq、Idris)和宏系统(模板 Haskell)为此增加了额外的复杂性,因为程序的某些部分可能需要编译 并执行以便对其他部分进行类型检查。
    • “执行这些动态类型检查的代码是解释器的一部分(如果有解释器)或由 JIT 编译器插入(即列表中的第 5 步)。” – 那么动态类型语言的 AOT 编译实现呢?例如。大多数 CommonLisps 都是 AOT 编译的,并且至少有一个 AOT 编译的 Ruby 实现(SAP 的 BlueRuby 用于 ABAP 平台)。在这种情况下,类型检查代码会被注入到编译后的可执行文件中。
    • @JörgWMittag 对,我不知道为什么我将语句限制为 JIT 编译器。
    猜你喜欢
    • 2011-05-23
    • 2012-06-10
    • 1970-01-01
    • 1970-01-01
    • 2020-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多