MIR 型チェック
借用チェックの主要なコンポーネントは MIR 型チェック です。 このチェックは MIR を走査し、完全な「型チェック」を行います – 他の言語にも見られるようなものと同じ種類のものです。 この型チェックを行う過程で、プログラムに適用されるリージョン制約も明らかにします。
TODO – さらに詳述する? たぶん? :)
ユーザー型
MIR 型チェックの開始時に、本体内のすべてのリージョンを、制約のない新しいリージョンに置き換えます。 しかし、これにより次のプログラムを受け入れてしまうことになります。
#![allow(unused)]
fn main() {
fn foo<'a>(x: &'a u32) {
let y: &'static u32 = x;
}
}
y の型のライフタイムを消去すると、それが 'static であるべきだということが分からなくなり、
ユーザーの意図を無視することになります。
これに対処するため、HIR 型チェック中にユーザーが明示的に型に言及したすべての場所を
CanonicalUserTypeAnnotations として記憶します。
注目するアノテーションには 2 種類あります。
- 明示的な型アスクリプション。たとえば
let y: &'static u32はUserType::Ty(&'static u32)になります。 - 明示的なジェネリック引数。たとえば
x.foo<&'a u32, Vec<String>>はUserType::TypeOf(foo_def_id, [&'a u32, Vec<String>])になります。
HIR 型チェックからのリージョン推論が MIR typeck に影響することは望ましくないため、
ユーザー型は HIR から lowering した直後に保存します。
これは、その型がまだ推論変数を含んでいる可能性があることを意味します。
そのため、正準ユーザー型アノテーションを使用しています。
代わりに、すべての推論変数を存在束縛変数に置き換えます。
したがって、let x: Vec<_> のようなものは exists<T> UserType::Ty(Vec<T>) になります。
let Foo(x): Foo<&'a u32> のようなパターンにはユーザー型 Foo<&'a u32> がありますが、
x の実際の型は &'a u32 のみであるべきです。このために、UserTypeProjection を使用します。
MIR では、ユーザー型をわずかに異なる 2 つの方法で扱います。
明示的な型注釈を持つパターン内の変数に対応する MIR ローカルが与えられた場合、
そのローカルの型が UserTypeProjection の型と等しいことを要求します。
これは LocalDecl に直接保存されます。
また、被検査式の型も制約します。たとえば let _: &'a u32 = x; における x の型です。
ここで T_x はユーザー型のサブタイプであればよいだけなので、その代わりに
StatementKind::AscribeUserType を使用します。
MIR 型チェッカーは型および const 推論変数を実際には扱わないため、
ユーザー型を直接使用しないことに注意してください。代わりに、HIR 型チェッカーからの最終的な
inferred_type を保存します。その後、MIR typeck 中にそのリージョンを新しい nll 推論変数に置き換え、
実際の UserType と関連付けることで、正しいリージョン制約を再び取得します。
MIR 型チェックの後、すべてのユーザー型アノテーションは不要になるため破棄されます。