|
| 1 | + |
| 2 | +# Coherence |
| 3 | + |
| 4 | +> NOTE: this is based on [notes by @lcnr](https://github.com/rust-lang/rust/pull/121848) |
| 5 | +
|
| 6 | +Coherence checking is what detects both of trait impls and inherent impls overlapping with others. |
| 7 | +(reminder: [inherent impls](https://doc.rust-lang.org/reference/items/implementations.html#inherent-implementations) are impls of concrete types like `impl MyStruct {}`) |
| 8 | + |
| 9 | +Overlapping trait impls always produce an error, |
| 10 | +while overlapping inherent impls result in an error only if they have methods with the same name. |
| 11 | + |
| 12 | +Checking for overlaps is split in two parts. First there's the [overlap check(s)](#overlap-checks), |
| 13 | +which finds overlaps between traits and inherent implementations that the compiler currently knows about. |
| 14 | + |
| 15 | +However, Coherence also results in an error if any other impls **could** exist, |
| 16 | +even if they are currently unknown. |
| 17 | +This affects impls which may get added to upstream crates in a backwards compatible way, |
| 18 | +and impls from downstream crates. |
| 19 | +This is called the Orphan check. |
| 20 | + |
| 21 | +## Overlap checks |
| 22 | + |
| 23 | +Overlap checks are performed for both inherent impls, and for trait impls. |
| 24 | +This uses the same overlap checking code, really done as two separate analyses. |
| 25 | +Overlap checks always consider pairs of implementations, comparing them to each other. |
| 26 | + |
| 27 | +Overlap checking for inherent impl blocks is done through `fn check_item` in coherence/inherent_impls_overlap.rs), |
| 28 | +where you can very clearly see that (at least for small `n`), the check really performs `n^2` |
| 29 | +comparisons between impls. |
| 30 | + |
| 31 | +In the case of traits, this check is currently done as part of building the [specialization graph](./specialization.md), |
| 32 | +to handle specializing impls overlapping with their parent, but this may change in the future. |
| 33 | + |
| 34 | +In both cases, all pairs of impls are checked for overlap. |
| 35 | + |
| 36 | +Overlapping is sometimes partially allowed: |
| 37 | + |
| 38 | +1. for maker traits |
| 39 | +2. under [specialization](./specialization.md) |
| 40 | + |
| 41 | +but normally isn't. |
| 42 | + |
| 43 | +The overlap check has various modes (see [`OverlapMode`]). |
| 44 | +Importantly, there's the explicit negative impl check, and the implicit negative impl check. |
| 45 | +Both try to prove that an overlap is definitely impossible. |
| 46 | + |
| 47 | +[`OverlapMode`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/traits/specialization_graph/enum.OverlapMode.html |
| 48 | + |
| 49 | +### The explicit negative impl check |
| 50 | + |
| 51 | +This check is done in [`impl_intersection_has_negative_obligation`]. |
| 52 | + |
| 53 | +This check tries to find a negative trait implementation. |
| 54 | +For example: |
| 55 | + |
| 56 | +```rust |
| 57 | +struct MyCustomErrorType; |
| 58 | + |
| 59 | +// both in your own crate |
| 60 | +impl From<&str> for MyCustomErrorType {} |
| 61 | +impl<E> From<E> for MyCustomErrorType where E: Error {} |
| 62 | +``` |
| 63 | + |
| 64 | +In this example, we'd get: |
| 65 | +`MyCustomErrorType: From<&str>` and `MyCustomErrorType: From<?E>`, giving `?E = &str`. |
| 66 | + |
| 67 | +And thus, these two implementations would overlap. |
| 68 | +However, libstd provides `&str: !Error`, and therefore guarantees that there |
| 69 | +will never be a positive implementation of `&str: Error`, and thus there is no overlap. |
| 70 | + |
| 71 | +Note that for this kind of negative impl check, we must have explicit negative implementations provided. |
| 72 | +This is not currently stable. |
| 73 | + |
| 74 | +[`impl_intersection_has_negative_obligation`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_trait_selection/traits/coherence/fn.impl_intersection_has_negative_obligation.html |
| 75 | + |
| 76 | +### The implicit negative impl check |
| 77 | + |
| 78 | +This check is done in [`impl_intersection_has_impossible_obligation`], |
| 79 | +and does not rely on negative trait implementations and is stable. |
| 80 | + |
| 81 | +Let's say there's a |
| 82 | +```rust |
| 83 | +impl From<MyLocalType> for Box<dyn Error> {} // in your own crate |
| 84 | +impl<E> From<E> for Box<dyn Error> where E: Error {} // in std |
| 85 | +``` |
| 86 | + |
| 87 | +This would give: `Box<dyn Error>: From<MyLocalType>`, and `Box<dyn Error>: From<?E>`, |
| 88 | +giving `?E = MyLocalType`. |
| 89 | + |
| 90 | +In your crate there's no `MyLocalType: Error`, downstream crates cannot implement `Error` (a remote trait) for `MyLocalType` (a remote type). |
| 91 | +Therefore, these two impls do not overlap. |
| 92 | +Importantly, this works even if there isn't a `impl !Error for MyLocalType`. |
| 93 | + |
| 94 | +[`impl_intersection_has_impossible_obligation`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_trait_selection/traits/coherence/fn.impl_intersection_has_impossible_obligation.html |
| 95 | + |
0 commit comments