]>
Commit | Line | Data |
---|---|---|
a1dfa0c6 XL |
1 | # Lowering rules |
2 | ||
3 | This section gives the complete lowering rules for Rust traits into | |
4 | [program clauses][pc]. It is a kind of reference. These rules | |
5 | reference the [domain goals][dg] defined in an earlier section. | |
6 | ||
7 | [pc]: ./goals-and-clauses.html | |
8 | [dg]: ./goals-and-clauses.html#domain-goals | |
9 | ||
10 | ## Notation | |
11 | ||
12 | The nonterminal `Pi` is used to mean some generic *parameter*, either a | |
13 | named lifetime like `'a` or a type parameter like `A`. | |
14 | ||
15 | The nonterminal `Ai` is used to mean some generic *argument*, which | |
16 | might be a lifetime like `'a` or a type like `Vec<A>`. | |
17 | ||
18 | When defining the lowering rules, we will give goals and clauses in | |
19 | the [notation given in this section](./goals-and-clauses.html). | |
20 | We sometimes insert "macros" like `LowerWhereClause!` into these | |
21 | definitions; these macros reference other sections within this chapter. | |
22 | ||
23 | ## Rule names and cross-references | |
24 | ||
25 | Each of these lowering rules is given a name, documented with a | |
26 | comment like so: | |
27 | ||
28 | // Rule Foo-Bar-Baz | |
29 | ||
30 | The reference implementation of these rules is to be found in | |
48663c56 XL |
31 | [`chalk/chalk-solve/src/clauses.rs`][chalk_rules]. They are also ported in |
32 | rustc in the [`librustc_traits`][librustc_traits] crate. | |
a1dfa0c6 | 33 | |
48663c56 | 34 | [chalk_rules]: https://github.com/rust-lang/chalk/blob/master/chalk-solve/src/clauses.rs |
a1dfa0c6 XL |
35 | [librustc_traits]: https://github.com/rust-lang/rust/tree/master/src/librustc_traits |
36 | ||
37 | ## Lowering where clauses | |
38 | ||
39 | When used in a goal position, where clauses can be mapped directly to | |
40 | the `Holds` variant of [domain goals][dg], as follows: | |
41 | ||
42 | - `A0: Foo<A1..An>` maps to `Implemented(A0: Foo<A1..An>)` | |
43 | - `T: 'r` maps to `Outlives(T, 'r)` | |
44 | - `'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)` | |
48 | ||
49 | In the rules below, we will use `WC` to indicate where clauses that | |
50 | appear in Rust syntax; we will then use the same `WC` to indicate | |
51 | where those where clauses appear as goals in the program clauses that | |
52 | we are producing. In that case, the mapping above is used to convert | |
53 | from the Rust syntax into goals. | |
54 | ||
55 | ### Transforming the lowered where clauses | |
56 | ||
57 | In addition, in the rules below, we sometimes do some transformations | |
58 | on the lowered where clauses, as defined here: | |
59 | ||
60 | - `FromEnv(WC)` – this indicates that: | |
61 | - `Implemented(TraitRef)` becomes `FromEnv(TraitRef)` | |
62 | - other where-clauses are left intact | |
63 | - `WellFormed(WC)` – this indicates that: | |
64 | - `Implemented(TraitRef)` becomes `WellFormed(TraitRef)` | |
65 | - other where-clauses are left intact | |
66 | ||
67 | *TODO*: I suspect that we want to alter the outlives relations too, | |
68 | but Chalk isn't modeling those right now. | |
69 | ||
70 | ## Lowering traits | |
71 | ||
72 | Given a trait definition | |
73 | ||
74 | ```rust,ignore | |
75 | trait Trait<P1..Pn> // P0 == Self | |
76 | where WC | |
77 | { | |
78 | // trait items | |
79 | } | |
80 | ``` | |
81 | ||
82 | we will produce a number of declarations. This section is focused on | |
83 | the program clauses for the trait header (i.e., the stuff outside the | |
84 | `{}`); the [section on trait items](#trait-items) covers the stuff | |
85 | inside the `{}`. | |
86 | ||
87 | ### Trait header | |
88 | ||
89 | From the trait itself we mostly make "meta" rules that setup the | |
90 | relationships between different kinds of domain goals. The first such | |
91 | rule from the trait header creates the mapping between the `FromEnv` | |
92 | and `Implemented` predicates: | |
93 | ||
94 | ```text | |
95 | // Rule Implemented-From-Env | |
96 | forall<Self, P1..Pn> { | |
97 | Implemented(Self: Trait<P1..Pn>) :- FromEnv(Self: Trait<P1..Pn>) | |
98 | } | |
99 | ``` | |
100 | ||
101 | <a name="implied-bounds"></a> | |
102 | ||
103 | #### Implied bounds | |
104 | ||
105 | The next few clauses have to do with implied bounds (see also | |
106 | [RFC 2089] and the [implied bounds][implied_bounds] chapter for a more in depth | |
107 | cover). For each trait, we produce two clauses: | |
108 | ||
109 | [RFC 2089]: https://rust-lang.github.io/rfcs/2089-implied-bounds.html | |
110 | [implied_bounds]: ./implied-bounds.md | |
111 | ||
112 | ```text | |
113 | // Rule Implied-Bound-From-Trait | |
114 | // | |
115 | // For each where clause WC: | |
116 | forall<Self, P1..Pn> { | |
e1599b0c | 117 | FromEnv(WC) :- FromEnv(Self: Trait<P1..Pn>) |
a1dfa0c6 XL |
118 | } |
119 | ``` | |
120 | ||
121 | This clause says that if we are assuming that the trait holds, then we can also | |
122 | assume that its where-clauses hold. It's perhaps useful to see an example: | |
123 | ||
124 | ```rust,ignore | |
125 | trait Eq: PartialEq { ... } | |
126 | ``` | |
127 | ||
128 | In this case, the `PartialEq` supertrait is equivalent to a `where | |
129 | Self: PartialEq` where clause, in our simplified model. The program | |
130 | clause above therefore states that if we can prove `FromEnv(T: Eq)` – | |
131 | e.g., if we are in some function with `T: Eq` in its where clauses – | |
132 | then we also know that `FromEnv(T: PartialEq)`. Thus the set of things | |
133 | that follow from the environment are not only the **direct where | |
134 | clauses** but also things that follow from them. | |
135 | ||
136 | The next rule is related; it defines what it means for a trait reference | |
137 | to be **well-formed**: | |
138 | ||
139 | ```text | |
140 | // Rule WellFormed-TraitRef | |
141 | forall<Self, P1..Pn> { | |
142 | WellFormed(Self: Trait<P1..Pn>) :- Implemented(Self: Trait<P1..Pn>) && WellFormed(WC) | |
143 | } | |
144 | ``` | |
145 | ||
146 | This `WellFormed` rule states that `T: Trait` is well-formed if (a) | |
147 | `T: Trait` is implemented and (b) all the where-clauses declared on | |
148 | `Trait` are well-formed (and hence they are implemented). Remember | |
149 | that the `WellFormed` predicate is | |
150 | [coinductive](./goals-and-clauses.html#coinductive); in this | |
151 | case, it is serving as a kind of "carrier" that allows us to enumerate | |
152 | all the where clauses that are transitively implied by `T: Trait`. | |
153 | ||
154 | An example: | |
155 | ||
156 | ```rust,ignore | |
157 | trait Foo: A + Bar { } | |
158 | trait Bar: B + Foo { } | |
159 | trait A { } | |
160 | trait B { } | |
161 | ``` | |
162 | ||
163 | Here, the transitive set of implications for `T: Foo` are `T: A`, `T: Bar`, and | |
164 | `T: B`. And indeed if we were to try to prove `WellFormed(T: Foo)`, we would | |
165 | have to prove each one of those: | |
166 | ||
167 | - `WellFormed(T: Foo)` | |
168 | - `Implemented(T: Foo)` | |
169 | - `WellFormed(T: A)` | |
170 | - `Implemented(T: A)` | |
171 | - `WellFormed(T: Bar)` | |
172 | - `Implemented(T: Bar)` | |
173 | - `WellFormed(T: B)` | |
174 | - `Implemented(T: Bar)` | |
175 | - `WellFormed(T: Foo)` -- cycle, true coinductively | |
176 | ||
177 | This `WellFormed` predicate is only used when proving that impls are | |
178 | well-formed – basically, for each impl of some trait ref `TraitRef`, | |
179 | we must show that `WellFormed(TraitRef)`. This in turn justifies the | |
180 | implied bounds rules that allow us to extend the set of `FromEnv` | |
181 | items. | |
182 | ||
183 | ## Lowering type definitions | |
184 | ||
185 | We also want to have some rules which define when a type is well-formed. | |
186 | For example, given this type: | |
187 | ||
188 | ```rust,ignore | |
189 | struct Set<K> where K: Hash { ... } | |
190 | ``` | |
191 | ||
192 | then `Set<i32>` is well-formed because `i32` implements `Hash`, but | |
193 | `Set<NotHash>` would not be well-formed. Basically, a type is well-formed | |
194 | if its parameters verify the where clauses written on the type definition. | |
195 | ||
196 | Hence, for every type definition: | |
197 | ||
198 | ```rust, ignore | |
199 | struct Type<P1..Pn> where WC { ... } | |
200 | ``` | |
201 | ||
202 | we produce the following rule: | |
203 | ||
204 | ```text | |
205 | // Rule WellFormed-Type | |
206 | forall<P1..Pn> { | |
207 | WellFormed(Type<P1..Pn>) :- WC | |
208 | } | |
209 | ``` | |
210 | ||
211 | Note that we use `struct` for defining a type, but this should be understood | |
212 | as a general type definition (it could be e.g. a generic `enum`). | |
213 | ||
214 | Conversely, we define rules which say that if we assume that a type is | |
215 | well-formed, we can also assume that its where clauses hold. That is, | |
216 | we produce the following family of rules: | |
217 | ||
218 | ```text | |
219 | // Rule Implied-Bound-From-Type | |
220 | // | |
221 | // For each where clause `WC` | |
222 | forall<P1..Pn> { | |
223 | FromEnv(WC) :- FromEnv(Type<P1..Pn>) | |
224 | } | |
225 | ``` | |
226 | ||
227 | As for the implied bounds RFC, functions will *assume* that their arguments | |
228 | are well-formed. For example, suppose we have the following bit of code: | |
229 | ||
230 | ```rust,ignore | |
231 | trait Hash: Eq { } | |
232 | struct Set<K: Hash> { ... } | |
233 | ||
234 | fn foo<K>(collection: Set<K>, x: K, y: K) { | |
235 | // `x` and `y` can be equalized even if we did not explicitly write | |
236 | // `where K: Eq` | |
237 | if x == y { | |
238 | ... | |
239 | } | |
240 | } | |
241 | ``` | |
242 | ||
243 | In the `foo` function, we assume that `Set<K>` is well-formed, i.e. we have | |
244 | `FromEnv(Set<K>)` in our environment. Because of the previous rule, we get | |
245 | `FromEnv(K: Hash)` without needing an explicit where clause. And because | |
246 | of the `Hash` trait definition, there also exists a rule which says: | |
247 | ||
248 | ```text | |
249 | forall<K> { | |
250 | FromEnv(K: Eq) :- FromEnv(K: Hash) | |
251 | } | |
252 | ``` | |
253 | ||
254 | which means that we finally get `FromEnv(K: Eq)` and then can compare `x` | |
255 | and `y` without needing an explicit where clause. | |
256 | ||
257 | <a name="trait-items"></a> | |
258 | ||
259 | ## Lowering trait items | |
260 | ||
261 | ### Associated type declarations | |
262 | ||
263 | Given a trait that declares a (possibly generic) associated type: | |
264 | ||
265 | ```rust,ignore | |
266 | trait Trait<P1..Pn> // P0 == Self | |
267 | where WC | |
268 | { | |
269 | type AssocType<Pn+1..Pm>: Bounds where WC1; | |
270 | } | |
271 | ``` | |
272 | ||
273 | We will produce a number of program clauses. The first two define | |
274 | the rules by which `ProjectionEq` can succeed; these two clauses are discussed | |
275 | in detail in the [section on associated types](./associated-types.html), | |
276 | but reproduced here for reference: | |
277 | ||
278 | ```text | |
279 | // Rule ProjectionEq-Normalize | |
280 | // | |
281 | // ProjectionEq can succeed by normalizing: | |
282 | forall<Self, P1..Pn, Pn+1..Pm, U> { | |
283 | ProjectionEq(<Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm> = U) :- | |
284 | Normalize(<Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm> -> U) | |
285 | } | |
286 | ``` | |
287 | ||
288 | ```text | |
289 | // Rule ProjectionEq-Placeholder | |
290 | // | |
291 | // ProjectionEq can succeed through the placeholder associated type, | |
292 | // see "associated type" chapter for more: | |
293 | forall<Self, P1..Pn, Pn+1..Pm> { | |
294 | ProjectionEq( | |
295 | <Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm> = | |
296 | (Trait::AssocType)<Self, P1..Pn, Pn+1..Pm> | |
297 | ) | |
298 | } | |
299 | ``` | |
300 | ||
301 | The next rule covers implied bounds for the projection. In particular, | |
302 | the `Bounds` declared on the associated type must have been proven to hold | |
303 | to show that the impl is well-formed, and hence we can rely on them | |
304 | elsewhere. | |
305 | ||
306 | ```text | |
307 | // Rule Implied-Bound-From-AssocTy | |
308 | // | |
309 | // For each `Bound` in `Bounds`: | |
310 | forall<Self, P1..Pn, Pn+1..Pm> { | |
311 | FromEnv(<Self as Trait<P1..Pn>>::AssocType<Pn+1..Pm>>: Bound) :- | |
312 | FromEnv(Self: Trait<P1..Pn>) && WC1 | |
313 | } | |
314 | ``` | |
315 | ||
316 | Next, we define the requirements for an instantiation of our associated | |
317 | type to be well-formed... | |
318 | ||
319 | ```text | |
320 | // Rule WellFormed-AssocTy | |
321 | forall<Self, P1..Pn, Pn+1..Pm> { | |
322 | WellFormed((Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>) :- | |
323 | Implemented(Self: Trait<P1..Pn>) && WC1 | |
324 | } | |
325 | ``` | |
326 | ||
327 | ...along with the reverse implications, when we can assume that it is | |
328 | well-formed. | |
329 | ||
330 | ```text | |
331 | // Rule Implied-WC-From-AssocTy | |
332 | // | |
333 | // For each where clause WC1: | |
334 | forall<Self, P1..Pn, Pn+1..Pm> { | |
335 | FromEnv(WC1) :- FromEnv((Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>) | |
336 | } | |
337 | ``` | |
338 | ||
339 | ```text | |
340 | // Rule Implied-Trait-From-AssocTy | |
341 | forall<Self, P1..Pn, Pn+1..Pm> { | |
342 | FromEnv(Self: Trait<P1..Pn>) :- | |
343 | FromEnv((Trait::AssocType)<Self, P1..Pn, Pn+1..Pm>) | |
344 | } | |
345 | ``` | |
346 | ||
347 | ### Lowering function and constant declarations | |
348 | ||
349 | Chalk didn't model functions and constants, but I would eventually like to | |
350 | treat them exactly like normalization. See [the section on function/constant | |
351 | values below](#constant-vals) for more details. | |
352 | ||
353 | ## Lowering impls | |
354 | ||
355 | Given an impl of a trait: | |
356 | ||
357 | ```rust,ignore | |
358 | impl<P0..Pn> Trait<A1..An> for A0 | |
359 | where WC | |
360 | { | |
361 | // zero or more impl items | |
362 | } | |
363 | ``` | |
364 | ||
365 | Let `TraitRef` be the trait reference `A0: Trait<A1..An>`. Then we | |
366 | will create the following rules: | |
367 | ||
368 | ```text | |
369 | // Rule Implemented-From-Impl | |
370 | forall<P0..Pn> { | |
371 | Implemented(TraitRef) :- WC | |
372 | } | |
373 | ``` | |
374 | ||
375 | In addition, we will lower all of the *impl items*. | |
376 | ||
377 | ## Lowering impl items | |
378 | ||
379 | ### Associated type values | |
380 | ||
381 | Given an impl that contains: | |
382 | ||
383 | ```rust,ignore | |
384 | impl<P0..Pn> Trait<P1..Pn> for P0 | |
385 | where WC_impl | |
386 | { | |
387 | type AssocType<Pn+1..Pm> = T; | |
388 | } | |
389 | ``` | |
390 | ||
391 | and our where clause `WC1` on the trait associated type from above, we | |
392 | produce the following rule: | |
393 | ||
394 | ```text | |
395 | // Rule Normalize-From-Impl | |
396 | forall<P0..Pm> { | |
397 | forall<Pn+1..Pm> { | |
398 | Normalize(<P0 as Trait<P1..Pn>>::AssocType<Pn+1..Pm> -> T) :- | |
399 | Implemented(P0 as Trait) && WC1 | |
400 | } | |
401 | } | |
402 | ``` | |
403 | ||
404 | Note that `WC_impl` and `WC1` both encode where-clauses that the impl can | |
405 | rely on. (`WC_impl` is not used here, because it is implied by | |
406 | `Implemented(P0 as Trait)`.) | |
407 | ||
408 | <a name="constant-vals"></a> | |
409 | ||
410 | ### Function and constant values | |
411 | ||
412 | Chalk didn't model functions and constants, but I would eventually | |
413 | like to treat them exactly like normalization. This presumably | |
414 | involves adding a new kind of parameter (constant), and then having a | |
415 | `NormalizeValue` domain goal. This is *to be written* because the | |
416 | details are a bit up in the air. |