HIR

HIR ——“高级中间表示” ——是大多数rustc组件中使用的主要IR。 它是抽象语法树(AST)的编译器友好表示形式,该结构在语法分析,宏扩展和名称解析之后生成(有关如何创建HIR,请参见Lowering)。 HIR的许多部分都非常类似于Rust表面语法,但是Rust的某些表达形式已被“去糖”。 例如,for循环将转换为了loop,因此不会出现在HIR中不会出现for。 这使HIR比普通AST更易于分析。

本章介绍了HIR的主要概念。

您可以通过将-Zunpretty=hir-tree标志传递给rustc来查看代码的HIR表示形式:

cargo rustc -- -Zunpretty=hir-tree

带外存储和Crate类型

HIR中的顶层数据结构是Crate,它存储当前正在编译的crate的内容(我们只为当前crate构造HIR)。 在AST中,crate数据结构基本上只包含根模块,而HIRCrate结构则包含许多map和其他用于组织crate内容以便于访问的数据。

例如,HIR中单个项目的内容(例如模块,功能,特征,隐含等)不能在父级中立即访问。 因此,例如,如果有一个包含函数bar()的模块项目foo


#![allow(unused_variables)]
fn main() {
mod foo {
    fn bar() { }
}
}

那么在模块foo的HIR中表示(Mod结构)中将只有bar()的**ItemId** I。 要获取函数bar()的详细信息,我们将在items映射中查找I

这种表示的一个很好的结果是,可以通过遍历这些映射中的键值对来遍历crate中的所有项目(而无需遍历整个HIR)。 对于trait项和impl项以及“实体”(如下所述)也有类似的map。

使用这种表示形式的另一个原因是为了更好地与增量编译集成。 这样,如果您想要访问&rustc_hir::Item(例如modfoo),则不能立即访问函数bar()的内容。 相反,您只能访问bar()id,并且必须调用要求id作为参数的某些函数来查找bar的内容。 这使编译器有机会观察到您访问了bar()的数据,然后记录依赖。

HIR中的标识符

大多数必须处理HIR中事物的代码都倾向于不携带对HIR的引用,而是携带标识符号(或“ids”)。现在,您会发现四种正在使用的标识符:

  • DefId,主要标识“定义”或顶层项目。
    • 您可以认为DefId是非常明确和完整路径的简写,例如std::collections::HashMap。 但是,这些路径能够命名在普通Rust中无法命名的东西(例如impls),并且它们还包含有关crate的其他信息(例如其版本号,因为同一crate的两个版本可以共存)。
    • 一个DefId实际上由两部分组成,一个CrateNum(标识一个crate)和一个DefIndex(索引到每个箱子中维护的item列表)。
  • HirId,它将特定item的索引与该item内的偏移量结合在一起。
    • HirId的关键点是它是相对于某些其他项目(通过DefId标识)的偏移量。
  • BodyId引用了板条箱中的特定项目的实际内容(函数或常量的定义)。 当前,它实际上是“ newtype'd”的 HirId
  • NodeId,它是一个绝对ID,用于标识HIR树中的单个节点。
    • 尽管这种标识符仍很常用,但它们正在被逐步淘汰
    • 由于它们在crate中是绝对的,因此在树中的任何位置添加新节点都会导致包装箱中所有后续代码的NodeId发生更改。 你可能已经看出来了,这对增量编译极为不利。

我们还有一个内部map,是从DefId到所谓的 “Def path”的映射。 “ Def path”就像一个模块路径,但是内容更为丰富。 例如,可能是crate::foo::MyStruct唯一标识此定义。 它与模块路径有些不同,因为它可能包含类型参数T,例如crate::foo::MyStruct::T,在普通的Rust中,就不能这么写。 这些用于增量编译。

HIR Map

在大多数情况下,当您使用HIR时,您将通过 HIR Map进行操作,该map可通过tcx.hir_map在tcx中访问(并在hir::map模块中定义)。 HIR map包含多种方法,用于在各种ID之间进行转换并查找与HIR节点关联的数据。

例如,如果您有DefId,并且想将其转换为NodeId,则可以使用tcx.hir.as_local_node_id(def_id)。 这将返回一个Option<NodeId> —— 如果def-id引用了当前crate之外的内容(因为这种内容没有HIR节点),则将为None; 否则返回Some(n),其中n是定义的节点ID。

同样,您可以使用tcx.hir.find(n)在节点上查找NodeId。 这将返回一个Option<Node<'tcx>>,其中Node是在map中定义的枚举。

通过对此进行匹配,您可以找出node-id所指的节点类型,并获得指向数据本身的指针。 通常,您知道节点n是哪种类型——例如 如果您知道n必须是某些HIR表达式, 则可以执行tcx.hir.expect_expr(n),它将提取并返回&hir::Expr,此时如果n实际上不是一个表达式,那么会panic。

最后,您可以通过tcx.hir.get_parent_node(n)之类的调用,使用HIR map来查找节点的父节点。

HIR Bodies

rustc_hir::Body代表某种可执行代码,例如函数/闭包的主体或常量的定义。 body与一个所有者相关联,“所有者”通常是某种Item(例如,fn()const),但也可以是闭包表达式(例如, |x, y| x + y)。 您可以使用HIR映射来查找与给定def-id(maybe_body_owned_by)关联的body,或找到body的所有者(body_owner_def_id)。