270 lines
8.9 KiB
Markdown
270 lines
8.9 KiB
Markdown
# The THIR
|
|
|
|
<!-- toc -->
|
|
|
|
The THIR ("Typed High-Level Intermediate Representation"), previously called HAIR for
|
|
"High-Level Abstract IR", is another IR used by rustc that is generated after
|
|
[type checking]. It is (as of <!-- date-check --> January 2024) used for
|
|
[MIR construction], [exhaustiveness checking], and [unsafety checking].
|
|
|
|
[type checking]: ./type-checking.md
|
|
[MIR construction]: ./mir/construction.md
|
|
[exhaustiveness checking]: ./pat-exhaustive-checking.md
|
|
[unsafety checking]: ./unsafety-checking.md
|
|
|
|
As the name might suggest, the THIR is a lowered version of the [HIR] where all
|
|
the types have been filled in, which is possible after type checking has completed.
|
|
But it has some other interesting features that distinguish it from the HIR:
|
|
|
|
- Like the MIR, the THIR only represents bodies, i.e. "executable code"; this includes
|
|
function bodies, but also `const` initializers, for example. Specifically, all [body owners] have
|
|
THIR created. Consequently, the THIR has no representation for items like `struct`s or `trait`s.
|
|
|
|
- Each body of THIR is only stored temporarily and is dropped as soon as it's no longer
|
|
needed, as opposed to being stored until the end of the compilation process (which
|
|
is what is done with the HIR).
|
|
|
|
- Besides making the types of all nodes available, the THIR also has additional
|
|
desugaring compared to the HIR. For example, automatic references and dereferences
|
|
are made explicit, and method calls and overloaded operators are converted into
|
|
plain function calls. Destruction scopes are also made explicit.
|
|
|
|
- Statements, expressions, and match arms are stored separately. For example, statements in the
|
|
`stmts` array reference expressions by their index (represented as a [`ExprId`]) in the `exprs`
|
|
array.
|
|
|
|
[HIR]: ./hir.md
|
|
[`ExprId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.ExprId.html
|
|
[body owners]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.BodyOwnerKind.html
|
|
|
|
The THIR lives in [`rustc_mir_build::thir`][thir-docs]. To construct a [`thir::Expr`],
|
|
you can use the [`thir_body`] function, passing in the memory arena where the THIR
|
|
will be allocated. Dropping this arena will result in the THIR being destroyed,
|
|
which is useful to keep peak memory in check. Having a THIR representation of
|
|
all bodies of a crate in memory at the same time would be very heavy.
|
|
|
|
You can get a debug representation of the THIR by passing the `-Zunpretty=thir-tree` flag
|
|
to `rustc`.
|
|
|
|
To demonstrate, let's use the following example:
|
|
|
|
```rust
|
|
fn main() {
|
|
let x = 1 + 2;
|
|
}
|
|
```
|
|
|
|
Here is how that gets represented in THIR (as of <!-- date-check --> Aug 2022):
|
|
|
|
```rust,no_run
|
|
Thir {
|
|
// no match arms
|
|
arms: [],
|
|
exprs: [
|
|
// expression 0, a literal with a value of 1
|
|
Expr {
|
|
ty: i32,
|
|
temp_lifetime: Some(
|
|
Node(1),
|
|
),
|
|
span: oneplustwo.rs:2:13: 2:14 (#0),
|
|
kind: Literal {
|
|
lit: Spanned {
|
|
node: Int(
|
|
1,
|
|
Unsuffixed,
|
|
),
|
|
span: oneplustwo.rs:2:13: 2:14 (#0),
|
|
},
|
|
neg: false,
|
|
},
|
|
},
|
|
// expression 1, scope surrounding literal 1
|
|
Expr {
|
|
ty: i32,
|
|
temp_lifetime: Some(
|
|
Node(1),
|
|
),
|
|
span: oneplustwo.rs:2:13: 2:14 (#0),
|
|
kind: Scope {
|
|
// reference to expression 0 above
|
|
region_scope: Node(3),
|
|
lint_level: Explicit(
|
|
HirId {
|
|
owner: DefId(0:3 ~ oneplustwo[6932]::main),
|
|
local_id: 3,
|
|
},
|
|
),
|
|
value: e0,
|
|
},
|
|
},
|
|
// expression 2, literal 2
|
|
Expr {
|
|
ty: i32,
|
|
temp_lifetime: Some(
|
|
Node(1),
|
|
),
|
|
span: oneplustwo.rs:2:17: 2:18 (#0),
|
|
kind: Literal {
|
|
lit: Spanned {
|
|
node: Int(
|
|
2,
|
|
Unsuffixed,
|
|
),
|
|
span: oneplustwo.rs:2:17: 2:18 (#0),
|
|
},
|
|
neg: false,
|
|
},
|
|
},
|
|
// expression 3, scope surrounding literal 2
|
|
Expr {
|
|
ty: i32,
|
|
temp_lifetime: Some(
|
|
Node(1),
|
|
),
|
|
span: oneplustwo.rs:2:17: 2:18 (#0),
|
|
kind: Scope {
|
|
region_scope: Node(4),
|
|
lint_level: Explicit(
|
|
HirId {
|
|
owner: DefId(0:3 ~ oneplustwo[6932]::main),
|
|
local_id: 4,
|
|
},
|
|
),
|
|
// reference to expression 2 above
|
|
value: e2,
|
|
},
|
|
},
|
|
// expression 4, represents 1 + 2
|
|
Expr {
|
|
ty: i32,
|
|
temp_lifetime: Some(
|
|
Node(1),
|
|
),
|
|
span: oneplustwo.rs:2:13: 2:18 (#0),
|
|
kind: Binary {
|
|
op: Add,
|
|
// references to scopes surrounding literals above
|
|
lhs: e1,
|
|
rhs: e3,
|
|
},
|
|
},
|
|
// expression 5, scope surrounding expression 4
|
|
Expr {
|
|
ty: i32,
|
|
temp_lifetime: Some(
|
|
Node(1),
|
|
),
|
|
span: oneplustwo.rs:2:13: 2:18 (#0),
|
|
kind: Scope {
|
|
region_scope: Node(5),
|
|
lint_level: Explicit(
|
|
HirId {
|
|
owner: DefId(0:3 ~ oneplustwo[6932]::main),
|
|
local_id: 5,
|
|
},
|
|
),
|
|
value: e4,
|
|
},
|
|
},
|
|
// expression 6, block around statement
|
|
Expr {
|
|
ty: (),
|
|
temp_lifetime: Some(
|
|
Node(9),
|
|
),
|
|
span: oneplustwo.rs:1:11: 3:2 (#0),
|
|
kind: Block {
|
|
body: Block {
|
|
targeted_by_break: false,
|
|
region_scope: Node(8),
|
|
opt_destruction_scope: None,
|
|
span: oneplustwo.rs:1:11: 3:2 (#0),
|
|
// reference to statement 0 below
|
|
stmts: [
|
|
s0,
|
|
],
|
|
expr: None,
|
|
safety_mode: Safe,
|
|
},
|
|
},
|
|
},
|
|
// expression 7, scope around block in expression 6
|
|
Expr {
|
|
ty: (),
|
|
temp_lifetime: Some(
|
|
Node(9),
|
|
),
|
|
span: oneplustwo.rs:1:11: 3:2 (#0),
|
|
kind: Scope {
|
|
region_scope: Node(9),
|
|
lint_level: Explicit(
|
|
HirId {
|
|
owner: DefId(0:3 ~ oneplustwo[6932]::main),
|
|
local_id: 9,
|
|
},
|
|
),
|
|
value: e6,
|
|
},
|
|
},
|
|
// destruction scope around expression 7
|
|
Expr {
|
|
ty: (),
|
|
temp_lifetime: Some(
|
|
Node(9),
|
|
),
|
|
span: oneplustwo.rs:1:11: 3:2 (#0),
|
|
kind: Scope {
|
|
region_scope: Destruction(9),
|
|
lint_level: Inherited,
|
|
value: e7,
|
|
},
|
|
},
|
|
],
|
|
stmts: [
|
|
// let statement
|
|
Stmt {
|
|
kind: Let {
|
|
remainder_scope: Remainder { block: 8, first_statement_index: 0},
|
|
init_scope: Node(1),
|
|
pattern: Pat {
|
|
ty: i32,
|
|
span: oneplustwo.rs:2:9: 2:10 (#0),
|
|
kind: Binding {
|
|
mutability: Not,
|
|
name: "x",
|
|
mode: ByValue,
|
|
var: LocalVarId(
|
|
HirId {
|
|
owner: DefId(0:3 ~ oneplustwo[6932]::main),
|
|
local_id: 7,
|
|
},
|
|
),
|
|
ty: i32,
|
|
subpattern: None,
|
|
is_primary: true,
|
|
},
|
|
},
|
|
initializer: Some(
|
|
e5,
|
|
),
|
|
else_block: None,
|
|
lint_level: Explicit(
|
|
HirId {
|
|
owner: DefId(0:3 ~ oneplustwo[6932]::main),
|
|
local_id: 6,
|
|
},
|
|
),
|
|
},
|
|
opt_destruction_scope: Some(
|
|
Destruction(1),
|
|
),
|
|
},
|
|
],
|
|
}
|
|
```
|
|
|
|
[thir-docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/index.html
|
|
[`thir::Expr`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/thir/struct.Expr.html
|
|
[`thir_body`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.thir_body
|