キャッシュとそれに関する微妙な考慮事項
一般に、私たちはトレイト選択の結果をキャッシュしようとします。これは いくらか複雑なプロセスです。その理由の一部は、トレイト参照内のすべての型が 完全には分かっていない場合でも結果をキャッシュできるようにしたいからです。 その場合、トレイト選択プロセスが型変数にも影響を与えていることがあるため、 選択プロセスの結果をキャッシュするだけでなく、型変数に対するその効果を 再生できる必要があります。
例
キャッシュがどのように機能するかの大まかな考え方は、まずすべての未束縛の
推論変数をプレースホルダー版に置き換えるというものです。したがって、
トレイト参照 usize : Foo<$t> があり、$t が未束縛の推論変数である場合、
それを usize : Foo<$0> に置き換えるかもしれません。ここで $0 は
プレースホルダー型です。その後、これをキャッシュで検索します。
ヒットが見つかった場合、そのヒットは選択プロセスで直ちに次に取るべき手順
(たとえば impl #22 を適用する、または where 句 X : Foo<Y> を適用する)
を教えてくれます。
一方、ヒットがない場合は、selection process を最初から実行する必要があります。たとえば、可能な impl は def-id 22 を持つ次のものだけである、という結論に達したとします。
impl Foo<isize> for usize { ... } // 実装 #22
その後、キャッシュに usize : Foo<$0> => ImplCandidate(22) を記録します。次に
ImplCandidate(22) を confirm します。これにより、副作用として
$t が isize と単一化されます。
さて、後のある時点で、usize : Foo<$u> に出くわしたとします。プレースホルダーに置き換えると、これは以前と同じく
usize : Foo<$0> となり、そのためキャッシュ検索は成功し、
ImplCandidate(22) が得られます。私たちは ImplCandidate(22) を confirm し、
これにより、副作用として $u が isize と単一化されます。
where 句とローカルキャッシュ対グローバルキャッシュ
微妙な相互作用の 1 つは、トレイト検索の結果が、どの where 句がスコープ内に
あるかによって変わるということです。したがって、実際には2 つのキャッシュ、
ローカルキャッシュとグローバルキャッシュがあります。ローカルキャッシュは
ParamEnv に付属し、グローバルキャッシュは tcx に付属します。
結果がスコープ内の where 句に依存する可能性がある場合には、ローカルキャッシュを
使用します。どちらのキャッシュを使用するかの決定は、select.rs の
pick_candidate_cache メソッドによって行われます。現時点では、非常に単純で
保守的な規則を使用しています。スコープ内に where 句が 1 つでもあれば、
ローカルキャッシュを使用します。以前は、より細かい区別を試みていましたが、
それにより #22019 や #18290 のような厄介で奇妙なバグが相次ぎました。
この単純な規則はかなり明確に安全であるように思われ、また非常に高いヒット率
(rustc のコンパイル時に約 95%)も維持しています。
TODO: pick_candidate_cache はもう存在しないようです。一般に、このセクションは
まだ少しでも正確なのでしょうか?