コヒーレンス
注記: これは @lcnr によるメモに基づいています
コヒーレンスチェックは、トレイト impl と固有 impl の両方が他と重複していることを検出するものです。
(リマインダー: 固有 impl は impl MyStruct {} のような具象型の impl です)
重複するトレイト impl は常にエラーを生成しますが、 重複する固有 impl は、同じ名前のメソッドを持つ場合にのみエラーになります。
重複のチェックは 2 つの部分に分かれています。 まず overlap check があります。 これは、コンパイラが現在把握しているトレイト実装と固有実装の間の重複を見つけます。
しかし、コヒーレンスは、現在は未知であっても、他の impl が存在し得る場合にもエラーになります。 これは、後方互換性を保った形で上流クレートに追加される可能性がある impl と、 下流クレートの impl に影響します。 これは Orphan チェックと呼ばれます。
Overlap checks
Overlap check は、固有 impl とトレイト impl の両方に対して実行されます。 これは同じ重複チェックコードを使用しますが、実際には 2 つの別々の解析として行われます。 Overlap check は常に実装のペアを考慮し、それらを互いに比較します。
固有 impl ブロックに対する重複チェックは fn check_item(coherence/inherent_impls_overlap.rs 内)を通じて行われます。
そこでは、(少なくとも小さい n については)チェックが実際に impl 間で n^2 回の
比較を実行していることが非常に明確にわかります。
トレイトの場合、このチェックは現在、特殊化グラフの構築の一部として行われています。 これは、特殊化する impl がその親と重複することを扱うためですが、将来変更される可能性があります。
どちらの場合も、すべての impl のペアについて重複がチェックされます。
重複は部分的に許可されることがあります。
- マーカートレイトの場合
- 特殊化のもとで
通常は許可されません。
Overlap check にはさまざまなモードがあります(OverlapMode を参照)。
重要なのは、明示的な negative impl チェックと、暗黙的な negative impl チェックがあることです。
どちらも、重複が絶対に不可能であることを証明しようとします。
明示的な negative impl チェック
このチェックは impl_intersection_has_negative_obligation で行われます。
このチェックは negative trait implementation を見つけようとします。 例:
#![allow(unused)]
fn main() {
struct MyCustomErrorType;
// どちらも自分のクレート内
impl From<&str> for MyCustomErrorType {}
impl<E> From<E> for MyCustomErrorType where E: Error {}
}
この例では、次のようになります:
MyCustomErrorType: From<&str> と MyCustomErrorType: From<?E> が得られ、?E = &str となります。
したがって、これら 2 つの実装は重複します。
しかし、libstd は &str: !Error を提供しており、そのため
&str: Error の positive implementation が決して存在しないことを保証します。したがって重複はありません。
この種の negative impl チェックでは、明示的な negative implementation が提供されていなければならないことに注意してください。 これは現在 stable ではありません。
暗黙的な negative impl チェック
このチェックは impl_intersection_has_impossible_obligation で行われます。
これは negative trait implementation に依存せず、stable です。
次のものがあるとします。
#![allow(unused)]
fn main() {
impl From<MyLocalType> for Box<dyn Error> {} // 自分のクレート内
impl<E> From<E> for Box<dyn Error> where E: Error {} // std 内
}
これにより、Box<dyn Error>: From<MyLocalType> と Box<dyn Error>: From<?E> が得られ、
?E = MyLocalType となります。
自分のクレートには MyLocalType: Error はなく、下流クレートは MyLocalType(リモート型)に対して Error(リモートトレイト)を実装できません。
したがって、これら 2 つの impl は重複しません。
重要なのは、これは impl !Error for MyLocalType が存在しない場合でも機能するということです。