Skip to content

Commit 1239ad3

Browse files
authored
add section on overlap checks (rust-lang#2042)
* add section on overlap checks * fix some typos * merge piece on overlap checks with docs about coherence (based on review comments) * fix comments after discussion
1 parent 7a37519 commit 1239ad3

File tree

4 files changed

+100
-2
lines changed

4 files changed

+100
-2
lines changed

src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@
160160
- [Type checking](./type-checking.md)
161161
- [Method Lookup](./method-lookup.md)
162162
- [Variance](./variance.md)
163+
- [Coherence Checking](./coherence.md)
163164
- [Opaque Types](./opaque-types-type-alias-impl-trait.md)
164165
- [Inference details](./opaque-types-impl-trait-inference.md)
165166
- [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)

src/coherence.md

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+

src/solve/invariants.md

+2
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,8 @@ in the trait solver
113113

114114
#### The type system is complete during the implicit negative overlap check in coherence ✅
115115

116+
For more on overlap checking: [../coherence.md]
117+
116118
During the implicit negative overlap check in coherence we must never return *error* for
117119
goals which can be proven. This would allow for overlapping impls with potentially different
118120
associated items, breaking a bunch of other invariants.

src/traits/specialization.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
Defined in the `specialize` module.
66

77
The basic strategy is to build up a *specialization graph* during
8-
coherence checking (recall that coherence checking looks for overlapping
9-
impls). Insertion into the graph locates the right place
8+
coherence checking (coherence checking looks for [overlapping impls](../coherence.md)).
9+
Insertion into the graph locates the right place
1010
to put an impl in the specialization hierarchy; if there is no right
1111
place (due to partial overlap but no containment), you get an overlap
1212
error. Specialization is consulted when selecting an impl (of course),

0 commit comments

Comments
 (0)