Skip to content

Commit fc7cb87

Browse files
committed
Auto merge of #630 - camelid:notation-docs, r=jackh726
Document notation Fixes #561.
2 parents a698320 + ef3b576 commit fc7cb87

File tree

3 files changed

+178
-116
lines changed

3 files changed

+178
-116
lines changed

book/src/SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
- [Opaque types (impl Trait)](./clauses/opaque_types.md)
2222
- [Well known traits](./clauses/well_known_traits.md)
2323
- [Well-formedness checking](./clauses/wf.md)
24-
- [Coherence](./coherence.md)
24+
- [Coherence](./clauses/coherence.md)
2525
- [Canonical queries](./canonical_queries.md)
2626
- [Canonicalization](./canonical_queries/canonicalization.md)
2727
- [Chalk engine](./engine.md)

book/src/clauses/coherence.md

Lines changed: 141 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Thus, it is probably best to look at the only *truly authoritative* source on th
6161

6262
## The Orphan Check in rustc
6363

64-
The orphan check as implemented today in the Rust compiler takes place in the `[orphan_check](https://github.com/rust-lang/rust/blob/b7c6e8f1805cd8a4b0a1c1f22f17a89e9e2cea23/src/librustc/traits/coherence.rs#L236)` function which is called [for every declared impl](https://github.com/rust-lang/rust/blob/b7c6e8f1805cd8a4b0a1c1f22f17a89e9e2cea23/src/librustc_typeck/coherence/orphan.rs#L45). Since implementations for locally defined traits are always defined, that function returns OK if the trait being implemented is local. Otherwise, it dispatches to the `[orphan_check_trait_ref](https://github.com/rust-lang/rust/blob/b7c6e8f1805cd8a4b0a1c1f22f17a89e9e2cea23/src/librustc/traits/coherence.rs#L343)` function which does the major orphan rules checking.
64+
The orphan check as implemented today in the Rust compiler takes place in the [`orphan_check`](https://github.com/rust-lang/rust/blob/b7c6e8f1805cd8a4b0a1c1f22f17a89e9e2cea23/src/librustc/traits/coherence.rs#L236) function which is called [for every declared impl](https://github.com/rust-lang/rust/blob/b7c6e8f1805cd8a4b0a1c1f22f17a89e9e2cea23/src/librustc_typeck/coherence/orphan.rs#L45). Since implementations for locally defined traits are always defined, that function returns OK if the trait being implemented is local. Otherwise, it dispatches to the [`orphan_check_trait_ref`](https://github.com/rust-lang/rust/blob/b7c6e8f1805cd8a4b0a1c1f22f17a89e9e2cea23/src/librustc/traits/coherence.rs#L343) function which does the major orphan rules checking.
6565

6666
Recall that the impls we are dealing with are in the form `impl<T0…Tn> Trait<P1…Pn> for P0`.
6767

@@ -90,37 +90,39 @@ Here’s how the lowering rules would look:
9090
For each trait `Trait`,
9191

9292
- If `Trait` is local to the current crate, we generate:
93-
forall<Self, P1...Pn> { LocalImplAllowed(Self: Trait<P1...Pn>) }
93+
`forall<Self, P1Pn> { LocalImplAllowed(Self: Trait<P1...Pn>) }`
9494
This models that any impls are allowed if the trait is local to the current crate.
9595
- If `Trait` is upstream to the current crate, we need a rule which models the additional conditions on which impls are allowed:
96-
forall<Self, P1...Pn> { LocalImplAllowed(Self: Trait<P1...Pn>) :- IsLocal(Self) }
97-
forall<Self, P1...Pn> {
98-
LocalImplAllowed(Self: Trait<P1...Pn>) :- IsFullyVisible(Self), IsLocal(P1)
99-
}
100-
forall<Self, P1...Pn> {
101-
LocalImplAllowed(Self: Trait<P1...Pn>) :-
102-
IsFullyVisible(Self),
103-
IsFullyVisible(P1),
104-
IsLocal(P2)
105-
}
106-
forall<Self, P1...Pn> {
107-
LocalImplAllowed(Self: Trait<P1...Pn>) :-
108-
IsFullyVisible(Self),
109-
IsFullyVisible(P1),
110-
IsFullyVisible(P2),
111-
IsLocal(P3)
112-
}
96+
```ignore
97+
forall<Self, P1...Pn> { LocalImplAllowed(Self: Trait<P1...Pn>) :- IsLocal(Self) }
98+
forall<Self, P1...Pn> {
99+
LocalImplAllowed(Self: Trait<P1...Pn>) :- IsFullyVisible(Self), IsLocal(P1)
100+
}
101+
forall<Self, P1...Pn> {
102+
LocalImplAllowed(Self: Trait<P1...Pn>) :-
103+
IsFullyVisible(Self),
104+
IsFullyVisible(P1),
105+
IsLocal(P2)
106+
}
107+
forall<Self, P1...Pn> {
108+
LocalImplAllowed(Self: Trait<P1...Pn>) :-
109+
IsFullyVisible(Self),
110+
IsFullyVisible(P1),
111+
IsFullyVisible(P2),
112+
IsLocal(P3)
113+
}
114+
...
115+
forall<Self, P1...Pn> {
116+
LocalImplAllowed(Self: Trait<P1...Pn>) :-
117+
IsFullyVisible(Self),
118+
IsFullyVisible(P1),
119+
IsFullyVisible(P2),
113120
...
114-
forall<Self, P1...Pn> {
115-
LocalImplAllowed(Self: Trait<P1...Pn>) :-
116-
IsFullyVisible(Self),
117-
IsFullyVisible(P1),
118-
IsFullyVisible(P2),
119-
...
120-
IsFullyVisible(Pn-1),
121-
IsLocal(Pn)
122-
}
123-
Here, we have modelled every possible case of `P1` to `Pn` being local and then checked if all prior type parameters are fully visible. This truly is a direct translation of the rules listed above!
121+
IsFullyVisible(Pn-1),
122+
IsLocal(Pn)
123+
}
124+
```
125+
Here, we have modelled every possible case of `P1` to `Pn` being local and then checked if all prior type parameters are fully visible. This truly is a direct translation of the rules listed above!
124126

125127
Now, to complete the orphan check, we can iterate over each impl of the same form as before and check if `LocalImplAllowed(P0: Trait<P1…Pn>)` is provable.
126128

@@ -131,16 +133,20 @@ The purpose of the overlap check is to ensure that there is only up to one impl
131133

132134
This is a simple application of the mathematical law:
133135

134-
> If two sets $$A$$ and $$B$$ are disjoint, then $$A\cap B = \emptyset$$
136+
> If two sets *A* and *B* are disjoint, then *A**B* =
135137
136138
More concretely, let’s say you have the following two impls: ([example from RFC 1023](https://rust-lang.github.io/rfcs/1023-rebalancing-coherence.html#type-locality-and-negative-reasoning))
137139

138-
impl<T: Copy> Clone for T {..}
139-
impl<U> Clone for MyType<U> {..}
140+
```rust,ignore
141+
impl<T: Copy> Clone for T { /* ... */ }
142+
impl<U> Clone for MyType<U> { /* ... */ }
143+
```
140144

141145
Then we’ll try to solve the following:
142146

143-
not { exists<T, U> { T = MyType<U>, T: Copy } }
147+
```ignore
148+
not { exists<T, U> { T = MyType<U>, T: Copy } }
149+
```
144150

145151
One way to read this is to say “try to prove that there is no `MyType<U>` for any `U` that implements the `Copy` trait”. The reason we’re trying to prove this is because if there is such an implementation, then the second impl would overlap with the first one. The first impl applies to any type that implements `Copy`.
146152

@@ -181,47 +187,51 @@ This significantly narrows down our set of potential impls that we need to accou
181187

182188
For downstream crates, we need to add rules for all possible impls that they could potentially add using any upstream traits or traits in the current crate. We can do this by enumerating the possibilities generated from the orphan rules specified above:
183189

184-
185-
// Given a trait MyTrait<P1...Pn> where WCs
186-
187-
forall<Self, P1...Pn> {
188-
Implemented(Self: MyTrait<P1...Pn>) :-
189-
WCs, // where clauses
190-
Compatible,
191-
DownstreamType(Self), // local to a downstream crate
192-
CannotProve,
193-
}
194-
forall<Self, P1...Pn> {
195-
Implemented(Self: MyTrait<P1...Pn>) :-
196-
WCs,
197-
Compatible,
198-
IsFullyVisible(Self),
199-
DownstreamType(P1),
200-
CannotProve,
201-
}
202-
...
203-
forall<Self, P1...Pn> {
204-
Implemented(Self: MyTrait<P1...Pn>) :-
205-
WCs,
206-
Compatible,
207-
IsFullyVisible(Self),
208-
IsFullyVisible(P1),
209-
...,
210-
IsFullyVisible(Pn-1),
211-
DownstreamType(Pn),
212-
CannotProve,
213-
}
190+
```ignore
191+
// Given a trait MyTrait<P1...Pn> where WCs
192+
193+
forall<Self, P1...Pn> {
194+
Implemented(Self: MyTrait<P1...Pn>) :-
195+
WCs, // where clauses
196+
Compatible,
197+
DownstreamType(Self), // local to a downstream crate
198+
CannotProve,
199+
}
200+
forall<Self, P1...Pn> {
201+
Implemented(Self: MyTrait<P1...Pn>) :-
202+
WCs,
203+
Compatible,
204+
IsFullyVisible(Self),
205+
DownstreamType(P1),
206+
CannotProve,
207+
}
208+
...
209+
forall<Self, P1...Pn> {
210+
Implemented(Self: MyTrait<P1...Pn>) :-
211+
WCs,
212+
Compatible,
213+
IsFullyVisible(Self),
214+
IsFullyVisible(P1),
215+
...,
216+
IsFullyVisible(Pn-1),
217+
DownstreamType(Pn),
218+
CannotProve,
219+
}
220+
```
214221

215222
Perhaps somewhat surprisingly, `IsFullyVisible` works here too. This is because our previous definition of the lowering for `IsFullyVisible` was quite broad. By lowering *all* types in the current crate and in upstream crates with `IsFullyVisible`, that predicate covers the correct set of types here too. The orphan rules only require that there are no types parameters prior to the first local type. Types that are not type parameters and also by definition not downstream types are all of the types in the current crate and in upstream crates. This is exactly what `IsFullyVisible` covers.
216223

217224
Fundamental types in both the current crate and in upstream crates can be considered local in a downstream crate if they are provided with a downstream type. To model this, we can add an additional rule for fundamental types:
218225

219-
220-
forall<T> { DownstreamType(MyFundamentalType<T>) :- DownstreamType(T) }
226+
```ignore
227+
forall<T> { DownstreamType(MyFundamentalType<T>) :- DownstreamType(T) }
228+
```
221229

222230
**Where clauses:** Traits can have where clauses.
223231

224-
#[upstream] trait Foo<T, U, V> where Self: Eq<T> { ... }
232+
```rust,ignore
233+
#[upstream] trait Foo<T, U, V> where Self: Eq<T> { /* ... */ }
234+
```
225235

226236
**The question is**: do we need to bring these where clauses down into the rule that we generate for the overlap check?
227237
**Answer:** Yes. Since the trait can only be implemented for types that satisfy its where clauses, it makes sense to also limit our assumption of compatible impls to impls that can exist.
@@ -234,60 +244,79 @@ Thus, based on the discussion above, the overlap check with coherence in mind ca
234244

235245

236246
- All disjoint queries take place inside of `compatible`
247+
237248
- `compatible { G }` desugars into `forall<T> { (Compatible, DownstreamType(T)) => G }`, thus introducing a `Compatible` predicate using implication
249+
238250
- For each upstream trait `MyTrait<P1…Pn>`, we lower it into the following rule:
239-
forall<Self, P1...Pn> {
240-
Implemented(Self: MyTrait<P1...Pn>) :-
241-
Compatible,
242-
IsUpstream(Self),
243-
IsUpstream(P1),
244-
...,
245-
IsUpstream(Pn),
246-
CannotProve
247-
}
248-
This will accomplish our goal of returning an ambiguous answer whenever the overlap check query asks about any impls that an upstream crate may add in a compatible way. We determined in the discussion above that these are the only impls in any crate that can be added compatibly.
249-
**Note:** Trait where clauses are lowered into the rule’s conditions as well as a prerequisite to everything else.
251+
252+
```ignore
253+
forall<Self, P1...Pn> {
254+
Implemented(Self: MyTrait<P1...Pn>) :-
255+
Compatible,
256+
IsUpstream(Self),
257+
IsUpstream(P1),
258+
...,
259+
IsUpstream(Pn),
260+
CannotProve
261+
}
262+
```
263+
264+
This will accomplish our goal of returning an ambiguous answer whenever the
265+
overlap check query asks about any impls that an upstream crate may add in a
266+
compatible way. We determined in the discussion above that these are the only
267+
impls in any crate that can be added compatibly.
268+
269+
**Note:** Trait `where` clauses are lowered into the rule’s conditions as well as a prerequisite to everything else.
270+
250271
- For all traits `MyTrait<P1…Pn> where WCs` in the current crate and in upstream traits,
251-
forall<Self, P1...Pn> {
252-
Implemented(Self: MyTrait<P1...Pn>) :-
253-
WCs, // where clauses
254-
Compatible,
255-
DownstreamType(Self), // local to a downstream crate
256-
CannotProve,
257-
}
258-
forall<Self, P1...Pn> {
259-
Implemented(Self: MyTrait<P1...Pn>) :-
260-
WCs,
261-
Compatible,
262-
IsFullyVisible(Self),
263-
DownstreamType(P1),
264-
CannotProve,
265-
}
266-
...
267-
forall<Self, P1...Pn> {
268-
Implemented(Self: MyTrait<P1...Pn>) :-
269-
WCs,
270-
Compatible,
271-
IsFullyVisible(Self),
272-
IsFullyVisible(P1),
273-
...,
274-
IsFullyVisible(Pn-1),
275-
DownstreamType(Pn),
276-
CannotProve,
277-
}
272+
```ignore
273+
forall<Self, P1...Pn> {
274+
Implemented(Self: MyTrait<P1...Pn>) :-
275+
WCs, // where clauses
276+
Compatible,
277+
DownstreamType(Self), // local to a downstream crate
278+
CannotProve,
279+
}
280+
forall<Self, P1...Pn> {
281+
Implemented(Self: MyTrait<P1...Pn>) :-
282+
WCs,
283+
Compatible,
284+
IsFullyVisible(Self),
285+
DownstreamType(P1),
286+
CannotProve,
287+
}
288+
...
289+
forall<Self, P1...Pn> {
290+
Implemented(Self: MyTrait<P1...Pn>) :-
291+
WCs,
292+
Compatible,
293+
IsFullyVisible(Self),
294+
IsFullyVisible(P1),
295+
...,
296+
IsFullyVisible(Pn-1),
297+
DownstreamType(Pn),
298+
CannotProve,
299+
}
300+
```
301+
278302
- For fundamental types in both the current crate and in upstream crates,
279-
forall<T> { DownstreamType(MyFundamentalType<T>) :- DownstreamType(T) }
303+
```ignore
304+
forall<T> { DownstreamType(MyFundamentalType<T>) :- DownstreamType(T) }
305+
```
306+
280307
## Alternative Designs
281308

282309
Initially, when Niko and I started working on this, Niko suggested the following implementation:
283310

284-
- For each upstream trait, `MyTrait<P1…Pn>`, we lower it into the following rule:
285-
forall<Self, P1...Pn> {
286-
Implemented(Self: MyTrait<P1...Pn>) :-
287-
Compatible,
288-
not { LocalImplAllowed(Self: MyTrait<P1...Pn>) },
289-
CannotProve
290-
}
311+
> For each upstream trait, `MyTrait<P1…Pn>`, we lower it into the following rule:
312+
> ```ignore
313+
> forall<Self, P1...Pn> {
314+
> Implemented(Self: MyTrait<P1...Pn>) :-
315+
> Compatible,
316+
> not { LocalImplAllowed(Self: MyTrait<P1...Pn>) },
317+
> CannotProve
318+
> }
319+
> ```
291320
292321
This appears to make sense because we need to assume that any impls that the current crate cannot add itself may exist somewhere else. By using `not { LocalImplAllowed(…) }`, we modelled exactly that. The problem is, that this assumption is actually too strong. What we actually need to model is that any **compatible** impls that the current crate cannot add itself may exist somewhere else. This is a **subset** of the impls covered by `not { LocalImplAllowed(…) }`.
293322

book/src/glossary.md

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
This is a glossary of terminology (possibly) used in the chalk crate.
44

5+
## Notation
6+
7+
### Basic notation
8+
9+
| Notation | Meaning |
10+
|--------------|-----------------------------------------|
11+
| `?0` | [Type inference variable] |
12+
| `^0`, `^1.0` | [Bound variable]; bound in a [`forall`] |
13+
| `!0`, `!1.0` | [Placeholder] |
14+
| `A :- B` | [Clause]; A is true if B is true |
15+
16+
### Rules
17+
18+
- `forall<T> { (Vec<T>: Clone) :- (T: Clone)`: for every `T`, `Vec<T>`
19+
implements `Clone` if `T` implements `Clone`
20+
21+
### Queries
22+
23+
- `Vec<i32>: Clone`: does `Vec<i32>` implement `Clone`?
24+
- `exists<T> { Vec<T>: Clone }`: does there exist a `T` such that `Vec<T>`
25+
implements `Clone`?
26+
27+
[Type inference variable]: ../types/rust_types.md#inference-variables
28+
[Bound variable]: ../types/rust_types.md#bound-variables
29+
[`forall`]: #debruijn-index
30+
[Placeholder]: ../types/rust_types.md#placeholders
31+
[Clause]: ../clauses/goals_and_clauses.md
32+
533
## Binary connective
634
There are sixteen logical connectives on two boolean variables. The most
735
interesting in this context are listed below. There is also a truth table given
@@ -50,15 +78,15 @@ the optional positive literal. Due to the equivalence `(P => Q) <=> (!P || Q)`
5078
the clause can be expressed as `B && C && ... => A` which means that A is true
5179
if `B`, `C`, etc. are all true. All rules in chalk are in this form. For example
5280

53-
```notrust
81+
```rust,ignore
5482
struct A<T> {}
5583
impl<T> B for A<T> where T: C + D {}
5684
```
5785

5886
is expressed as the *Horn clause* `(T: C) && (T: D) => (A<T>: B)`. This formula
5987
has to hold for all values of `T`. The second example
6088

61-
```notrust
89+
```rust,ignore
6290
struct A {}
6391
impl B for A {}
6492
impl C for A {}
@@ -76,6 +104,11 @@ Given the example `forall<T> { exists<U> { T: Foo<Item=U> } }` the
76104
literal names `U` and `T` are replaced with `0` and `1` respectively and the names are erased from the binders: `forall<_>
77105
{ exists<_> { 1: Foo<Item=0> } }`.
78106

107+
As another example, in `forall<X, Y> { forall <Z> { X } }`, `X` is represented
108+
as `^1.0`. The `1` represents the de Bruijn index of the variable and the `0`
109+
represents the index in that scope: `X` is bound in the second scope counting
110+
from where it is referenced, and it is the first variable bound in that scope.
111+
79112
## Formula
80113
A formula is a logical expression consisting of literals and constants connected
81114
by logical operators.
@@ -149,7 +182,7 @@ syntactic rules.
149182
In the context of the Rust type system this means that basic rules for type
150183
construction have to be met. Two examples: 1) Given a struct definition
151184

152-
```notrust
185+
```rust,ignore
153186
struct HashSet<T: Hash>
154187
```
155188
then a type `HashSet<i32>` is well-formed since `i32` implements `Hash`. A type

0 commit comments

Comments
 (0)