Skip to content

Commit e074d40

Browse files
committed
Polish lowering chapters and update rules
1 parent ceff08f commit e074d40

File tree

4 files changed

+115
-39
lines changed

4 files changed

+115
-39
lines changed

src/traits/associated-types.md

+15-6
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ type.)
6767
We could apply that rule to normalize either of the examples that
6868
we've seen so far.
6969

70-
## Skolemized associated types
70+
## Placeholder associated types
7171

7272
Sometimes however we want to work with associated types that cannot be
7373
normalized. For example, consider this function:
@@ -78,28 +78,37 @@ fn foo<T: IntoIterator>(...) { ... }
7878

7979
In this context, how would we normalize the type `T::Item`? Without
8080
knowing what `T` is, we can't really do so. To represent this case, we
81-
introduce a type called a **skolemized associated type
81+
introduce a type called a **placeholder associated type
8282
projection**. This is written like so `(IntoIterator::Item)<T>`. You
8383
may note that it looks a lot like a regular type (e.g., `Option<T>`),
8484
except that the "name" of the type is `(IntoIterator::Item)`. This is
85-
not an accident: skolemized associated type projections work just like
85+
not an accident: placeholder associated type projections work just like
8686
ordinary types like `Vec<T>` when it comes to unification. That is,
8787
they are only considered equal if (a) they are both references to the
8888
same associated type, like `IntoIterator::Item` and (b) their type
8989
arguments are equal.
9090

91-
Skolemized associated types are never written directly by the user.
91+
Placeholder associated types are never written directly by the user.
9292
They are used internally by the trait system only, as we will see
9393
shortly.
9494

95+
In rustc, they correspond to the `TyKind::UnnormalizedProjectionTy` enum
96+
variant, declared in [`librustc/ty/sty.rs`][sty]. In chalk, we use an
97+
`ApplicationTy` with a name living in a special namespace dedicated to
98+
placeholder associated types (see the `TypeName` enum declared in
99+
[`chalk-ir/src/lib.rs`][chalk_type_name]).
100+
101+
[sty]: https://github.com/rust-lang/rust/blob/master/src/librustc/ty/sty.rs
102+
[chalk_type_name]: https://github.com/rust-lang-nursery/chalk/blob/master/chalk-ir/src/lib.rs
103+
95104
## Projection equality
96105

97106
So far we have seen two ways to answer the question of "When can we
98107
consider an associated type projection equal to another type?":
99108

100109
- the `Normalize` predicate could be used to transform associated type
101110
projections when we knew which impl was applicable;
102-
- **skolemized** associated types can be used when we don't.
111+
- **placeholder** associated types can be used when we don't.
103112

104113
We now introduce the `ProjectionEq` predicate to bring those two cases
105114
together. The `ProjectionEq` predicate looks like so:
@@ -109,7 +118,7 @@ ProjectionEq(<T as IntoIterator>::Item = U)
109118
```
110119

111120
and we will see that it can be proven *either* via normalization or
112-
skolemization. As part of lowering an associated type declaration from
121+
via the placeholder type. As part of lowering an associated type declaration from
113122
some trait, we create two program clauses for `ProjectionEq`:
114123

115124
```text

src/traits/goals-and-clauses.md

+65-18
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,33 @@ paper
3737
["A Proof Procedure for the Logic of Hereditary Harrop Formulas"][pphhf]
3838
gives the details.
3939

40+
In terms of code, these types are defined in
41+
[`librustc/traits/mod.rs`][traits_mod] in rustc, and in
42+
[`chalk-ir/src/lib.rs`][chalk_ir] in chalk.
43+
4044
[pphhf]: ./bibliography.html#pphhf
45+
[traits_mod]: https://github.com/rust-lang/rust/blob/master/src/librustc/traits/mod.rs
46+
[chalk_ir]: https://github.com/rust-lang-nursery/chalk/blob/master/chalk-ir/src/lib.rs
4147

4248
<a name="domain-goals"></a>
4349

4450
## Domain goals
4551

52+
*Domain goals* are the atoms of the trait logic. As can be seen in the
53+
definitions given above, general goals basically consist in a combination of
54+
domain goals.
55+
56+
Moreover, flattenning a bit the definition of clauses given previously, one can
57+
see that clauses are always of the form:
58+
```text
59+
forall<K1, ..., Kn> { DomainGoal :- Goal }
60+
```
61+
hence domain goals are in fact clauses LHS. That is, at the most granular level,
62+
domain goals are what the trait solver will end up trying to prove.
63+
4664
<a name="trait-ref"></a>
4765

48-
To define the set of *domain goals* in our system, we need to first
66+
To define the set of domain goals in our system, we need to first
4967
introduce a few simple formulations. A **trait reference** consists of
5068
the name of a trait along with a suitable set of inputs P0..Pn:
5169

@@ -70,18 +88,24 @@ Projection = <P0 as TraitName<P1..Pn>>::AssocItem<Pn+1..Pm>
7088
Given these, we can define a `DomainGoal` as follows:
7189

7290
```text
73-
DomainGoal = Implemented(TraitRef)
74-
| ProjectionEq(Projection = Type)
75-
| Normalize(Projection -> Type)
91+
DomainGoal = Holds(WhereClause)
7692
| FromEnv(TraitRef)
77-
| FromEnv(Projection = Type)
78-
| WellFormed(Type)
93+
| FromEnv(Type)
7994
| WellFormed(TraitRef)
80-
| WellFormed(Projection = Type)
95+
| WellFormed(Type)
96+
| Normalize(Projection -> Type)
97+
98+
WhereClause = Implemented(TraitRef)
99+
| ProjectionEq(Projection = Type)
81100
| Outlives(Type: Region)
82101
| Outlives(Region: Region)
83102
```
84103

104+
`WhereClause` refers to a `where` clause that a Rust user would actually be able
105+
to write in a Rust program. This abstraction exists only as a convenience as we
106+
sometimes want to only coope with domain goals that are effectively writable in
107+
Rust.
108+
85109
Let's break down each one of these, one-by-one.
86110

87111
#### Implemented(TraitRef)
@@ -109,12 +133,10 @@ also requires proving `Implemented(T: Trait)`.
109133
[n]: ./associated-types.html#normalize
110134
[at]: ./associated-types.html
111135

112-
#### FromEnv(TraitRef), FromEnv(Projection = Type)
136+
#### FromEnv(TraitRef)
113137
e.g. `FromEnv(Self: Add<i32>)`
114138

115-
e.g. `FromEnv(<Self as StreamingIterator>::Item<'a> = &'a [u8])`
116-
117-
True if the inner `TraitRef` or projection equality is *assumed* to be true;
139+
True if the inner `TraitRef` is *assumed* to be true,
118140
that is, if it can be derived from the in-scope where clauses.
119141

120142
For example, given the following function:
@@ -131,24 +153,49 @@ where clauses nest, so a function body inside an impl body inherits the
131153
impl body's where clauses, too.
132154

133155
This and the next rule are used to implement [implied bounds]. As we'll see
134-
in the section on lowering, `FromEnv(X)` implies `Implemented(X)`, but not
135-
vice versa. This distinction is crucial to implied bounds.
156+
in the section on lowering, `FromEnv(TraitRef)` implies `Implemented(TraitRef)`,
157+
but not vice versa. This distinction is crucial to implied bounds.
158+
159+
#### FromEnv(Type)
160+
e.g. `FromEnv(HashSet<K>)`
161+
162+
True if the inner `Type` is *assumed* to be well-formed, that is, if it is an
163+
input type of a function or an impl.
164+
165+
For example, given the following code:
166+
167+
```rust,ignore
168+
struct HashSet<K> where K: Hash { ... }
169+
170+
fn loud_insert<K>(set: &mut HashSet<K>, item: K) {
171+
println!("inserting!");
172+
set.insert(item);
173+
}
174+
```
175+
176+
`HashSet<K>` is an input type of the `loud_insert` function. Hence, we assume it
177+
to be well-formed, so we would have `FromEnv(HashSet<K>)` inside the body or our
178+
function. Here `FromEnv(HashSet<K>)` implies `Implemented(K: Hash)` because the
179+
`HashSet` declaration was written with a `K: Hash` where clause. Hence, we don't
180+
need to repeat that bound on the `loud_insert` function: we rather automatically
181+
assume that it is true.
136182

137183
#### WellFormed(Item)
138184
These goals imply that the given item is *well-formed*.
139185

140186
We can talk about different types of items being well-formed:
141187

142-
**Types**, like `WellFormed(Vec<i32>)`, which is true in Rust, or
188+
* *Types*, like `WellFormed(Vec<i32>)`, which is true in Rust, or
143189
`WellFormed(Vec<str>)`, which is not (because `str` is not `Sized`.)
144190

145-
**TraitRefs**, like `WellFormed(Vec<i32>: Clone)`.
146-
147-
**Projections**, like `WellFormed(T: Iterator<Item = u32>)`.
191+
* *TraitRefs*, like `WellFormed(Vec<i32>: Clone)`.
148192

149193
Well-formedness is important to [implied bounds]. In particular, the reason
150-
it is okay to assume `FromEnv(T: Clone)` in the example above is that we
194+
it is okay to assume `FromEnv(T: Clone)` in the `loud_clone` example is that we
151195
_also_ verify `WellFormed(T: Clone)` for each call site of `loud_clone`.
196+
Similarly, it is okay to assume `FromEnv(HashSet<K>)` in the `loud_insert`
197+
example because we will verify `WellFormed(HashSet<K>)` for each call site of
198+
`loud_insert`.
152199

153200
#### Outlives(Type: Region), Outlives(Region: Region)
154201
e.g. `Outlives(&'a str: 'b)`, `Outlives('a: 'static)`

src/traits/index.md

+14
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@ Trait solving is based around a few key ideas:
3333
constraints can be checked by thet type checker.
3434

3535
Note: this is not a complete list of topics. See the sidebar for more.
36+
37+
The design of the new-style trait solving currently happens in two places:
38+
* The [chalk][chalk] repository is where we experiment with new ideas and
39+
designs for the trait system. It basically consists of a unit testing framework
40+
for the correctness and feasibility of the logical rules defining the new-style
41+
trait system. It also provides the [`chalk_engine`][chalk_engine] crate, which
42+
defines the new-style trait solver used both in the unit testing framework and
43+
in rustc.
44+
* Once we are happy with the logical rules, we proceed to implementing them in
45+
rustc. This mainly happens in [`librustc_traits`][librustc_traits].
46+
47+
[chalk]: https://github.com/rust-lang-nursery/chalk
48+
[chalk_engine]: https://github.com/rust-lang-nursery/chalk/tree/master/chalk-engine
49+
[librustc_traits]: https://github.com/rust-lang/rust/tree/master/src/librustc_traits

src/traits/lowering-rules.md

+21-15
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,24 @@ comment like so:
2727

2828
// Rule Foo-Bar-Baz
2929

30-
you can also search through the `librustc_traits` crate in rustc
31-
to find the corresponding rules from the implementation.
30+
The reference implementation of these rules is to be found in
31+
[`chalk/src/rules.rs`][chalk_rules]. They are also ported in rustc in the
32+
[`librustc_traits`][librustc_traits] crate.
33+
34+
[chalk_rules]: https://github.com/rust-lang-nursery/chalk/blob/master/src/rules.rs
35+
[librustc_traits]: https://github.com/rust-lang/rust/tree/master/src/librustc_traits
3236

3337
## Lowering where clauses
3438

3539
When used in a goal position, where clauses can be mapped directly to
36-
[domain goals][dg], as follows:
40+
the `Holds` variant of [domain goals][dg], as follows:
3741

38-
- `A0: Foo<A1..An>` maps to `Implemented(A0: Foo<A1..An>)`.
39-
- `A0: Foo<A1..An, Item = T>` maps to
40-
`ProjectionEq(<A0 as Foo<A1..An>>::Item = T)`
42+
- `A0: Foo<A1..An>` maps to `Implemented(A0: Foo<A1..An>)`
4143
- `T: 'r` maps to `Outlives(T, 'r)`
4244
- `'a: 'b` maps to `Outlives('a, 'b)`
45+
- `A0: Foo<A1..An, Item = T>` is a bit special and expands to two distinct
46+
goals, namely `Implemented(A0: Foo<A1..An>)` and
47+
`ProjectionEq(<A0 as Foo<A1..An>>::Item = T)`
4348

4449
In the rules below, we will use `WC` to indicate where clauses that
4550
appear in Rust syntax; we will then use the same `WC` to indicate
@@ -54,11 +59,10 @@ on the lowered where clauses, as defined here:
5459

5560
- `FromEnv(WC)` – this indicates that:
5661
- `Implemented(TraitRef)` becomes `FromEnv(TraitRef)`
57-
- `ProjectionEq(Projection = Ty)` becomes `FromEnv(Projection = Ty)`
5862
- other where-clauses are left intact
5963
- `WellFormed(WC)` – this indicates that:
6064
- `Implemented(TraitRef)` becomes `WellFormed(TraitRef)`
61-
- `ProjectionEq(Projection = Ty)` becomes `WellFormed(Projection = Ty)`
65+
- other where-clauses are left intact
6266

6367
*TODO*: I suspect that we want to alter the outlives relations too,
6468
but Chalk isn't modeling those right now.
@@ -99,9 +103,11 @@ forall<Self, P1..Pn> {
99103
#### Implied bounds
100104

101105
The next few clauses have to do with implied bounds (see also
102-
[RFC 2089]). For each trait, we produce two clauses:
106+
[RFC 2089] and the [implied bounds][implied_bounds] chapter for a more in depth
107+
cover). For each trait, we produce two clauses:
103108

104109
[RFC 2089]: https://rust-lang.github.io/rfcs/2089-implied-bounds.html
110+
[implied_bounds]: ./implied-bounds.md
105111

106112
```text
107113
// Rule Implied-Bound-From-Trait
@@ -210,7 +216,7 @@ well-formed, we can also assume that its where clauses hold. That is,
210216
we produce the following family of rules:
211217

212218
```text
213-
// Rule FromEnv-Type
219+
// Rule Implied-Bound-From-Type
214220
//
215221
// For each where clause `WC`
216222
forall<P1..Pn> {
@@ -280,10 +286,10 @@ forall<Self, P1..Pn, Pn+1..Pm, U> {
280286
```
281287

282288
```text
283-
// Rule ProjectionEq-Skolemize
289+
// Rule ProjectionEq-Placeholder
284290
//
285-
// ProjectionEq can succeed by skolemizing, see "associated type"
286-
// chapter for more:
291+
// ProjectionEq can succeed through the placeholder associated type,
292+
// see "associated type" chapter for more:
287293
forall<Self, P1..Pn, Pn+1..Pm> {
288294
ProjectionEq(
289295
<Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm> =
@@ -303,7 +309,7 @@ elsewhere.
303309
// For each `Bound` in `Bounds`:
304310
forall<Self, P1..Pn, Pn+1..Pm> {
305311
FromEnv(<Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm>>: Bound) :-
306-
FromEnv(Self: Trait<P1..Pn>)
312+
FromEnv(Self: Trait<P1..Pn>), FromEnv(WC1)
307313
}
308314
```
309315

@@ -314,7 +320,7 @@ type to be well-formed...
314320
// Rule WellFormed-AssocTy
315321
forall<Self, P1..Pn, Pn+1..Pm> {
316322
WellFormed((Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>) :-
317-
WC1, Implemented(Self: Trait<P1..Pn>)
323+
Implemented(Self: Trait<P1..Pn>), WC1
318324
}
319325
```
320326

0 commit comments

Comments
 (0)