THIR
THIR(“Typed High-Level Intermediate Representation”)は、以前は “High-Level Abstract IR” の略として HAIR と呼ばれていた、rustc で使われるもう一つの IR であり、 型検査の後に生成されます。これは( 2024年1月時点で) MIR 構築、網羅性検査、および unsafe 性検査に使われています。
名前から推測できるかもしれませんが、THIR は HIR を低水準化したバージョンであり、 すべての型が埋められています。これは型検査が完了した後に可能になります。 しかし、THIR には HIR と区別される、他にも興味深い特徴があります。
-
MIR と同様に、THIR はボディ、つまり「実行可能なコード」のみを表します。これには 関数ボディだけでなく、たとえば
const初期化子も含まれます。 具体的には、すべてのボディ所有者について THIR が作成されます。 したがって、THIR にはstructやtraitのようなアイテムを表すものはありません。 -
THIR の各ボディは一時的にのみ保存され、不要になり次第すぐに破棄されます。 これは、コンパイルプロセスの最後まで保存される場合(HIR で行われていること)とは対照的です。
-
THIR では、すべてのノードの型を利用可能にすることに加えて、HIR と比べて追加の 脱糖も行われています。 たとえば、自動的な参照とデリファレンスは 明示化され、メソッド呼び出しとオーバーロードされた演算子は通常の関数呼び出しに変換されます。 破棄スコープも明示化されます。
-
文、式、match アーム、ブロック、パラメータは別々に保存されます。 たとえば、
stmts配列内の文は、exprs配列内の式をそのインデックス(ExprIdとして表されます)で参照します。
THIR は rustc_mir_build::thir にあります。
thir::Expr を構築するには、
THIR が割り当てられるメモリアリーナを渡して、thir_body 関数を使うことができます。
このアリーナを破棄すると THIR も破棄されます。
これはピークメモリを抑えるのに役立ちます。
クレートのすべてのボディの THIR 表現を同時にメモリ上に保持するのは、非常に重くなります。
rustc に -Zunpretty=thir-tree フラグを渡すことで、THIR のデバッグ表現を取得できます。
実例として、次の例を使ってみましょう。
fn main() {
let x = 1 + 2;
}
これが THIR でどのように表現されるかを以下に示します( 2022年8月時点)。
```rust,no_run
Thir {
// match アームなし
arms: [],
exprs: [
// 式 0、値 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,
},
},
// 式 1、リテラル 1 を囲むスコープ
Expr {
ty: i32,
temp_lifetime: Some(
Node(1),
),
span: oneplustwo.rs:2:13: 2:14 (#0),
kind: Scope {
// 上記の式 0 への参照
region_scope: Node(3),
lint_level: Explicit(
HirId {
owner: DefId(0:3 ~ oneplustwo[6932]::main),
local_id: 3,
},
),
value: e0,
},
},
// 式 2、リテラル 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,
},
},
// 式 3、リテラル 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,
},
),
// 上記の式 2 への参照
value: e2,
},
},
// 式 4、1 + 2 を表す
Expr {
ty: i32,
temp_lifetime: Some(
Node(1),
),
span: oneplustwo.rs:2:13: 2:18 (#0),
kind: Binary {
op: Add,
// 上記のリテラルを囲むスコープへの参照
lhs: e1,
rhs: e3,
},
},
// 式 5、式 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,
},
},
// 式 6、文を囲むブロック
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),
// 下記の文 0 への参照
stmts: [
s0,
],
expr: None,
safety_mode: Safe,
},
},
},
// 式 7、式 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,
},
},
// 式 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 文
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),
),
},
],
}