Skip to content

Commit 9b6d561

Browse files
committed
SLG: Add links and mark-i-m's suggestions
1 parent 62c8d04 commit 9b6d561

File tree

1 file changed

+68
-49
lines changed

1 file changed

+68
-49
lines changed

src/traits/slg.md

+68-49
Original file line numberDiff line numberDiff line change
@@ -16,43 +16,45 @@ a set of region constraints, which we'll ignore in this introduction.
1616

1717
There are often many, or even infinitely many, solutions to a query. For
1818
example, say we want to prove that `exists<T> { Vec<T>: Debug }` for _some_
19-
type `?T`. Our solver should be capable of iterating over each answer one at
20-
a time, say `?T = u32`, then `?T = i32`, and so on, rather than iterating
21-
over every type in the type system. If we need more answers, we can request
22-
more until we are done. This is similar to how Prolog works.
19+
type `?T`. Our solver should be capable of yielding one answer at a time, say
20+
`?T = u32`, then `?T = i32`, and so on, rather than iterating over every type
21+
in the type system. If we need more answers, we can request more until we are
22+
done. This is similar to how Prolog works.
2323

2424
*See also: [The traditional, interactive Prolog query][pq]*
2525

2626
[pq]: ./canonical-queries.html#the-traditional-interactive-prolog-query
2727

2828
### Breadth-first
2929

30-
`Vec<?T>: Debug` is true if `?T: Debug`. This leads to a cycle: `[Vec<u32> ,
30+
`Vec<?T>: Debug` is true if `?T: Debug`. This leads to a cycle: `[Vec<u32>,
3131
Vec<Vec<u32>>, Vec<Vec<Vec<u32>>>]`, and so on all implement `Debug`. Our
3232
solver ought to be breadth first and consider answers like `[Vec<u32>: Debug,
3333
Vec<i32>: Debug, ...]` before it recurses, or we may never find the answer
3434
we're looking for.
3535

36-
### Cache friendly
36+
### Cachable
3737

3838
To speed up compilation, we need to cache results, including partial results
3939
left over from past solver queries.
4040

4141
## Description of how it works
4242

43-
The basis of the solver is the `Forest` type. A *forest* stores a
43+
The basis of the solver is the [`Forest`] type. A *forest* stores a
4444
collection of *tables* as well as a *stack*. Each *table* represents
4545
the stored results of a particular query that is being performed, as
4646
well as the various *strands*, which are basically suspended
4747
computations that may be used to find more answers. Tables are
4848
interdependent: solving one query may require solving others.
4949

50+
[`Forest`]: https://rust-lang-nursery.github.io/chalk/doc/chalk_engine/forest/struct.Forest.html
51+
5052
### Walkthrough
5153

5254
Perhaps the easiest way to explain how the solver works is to walk
5355
through an example. Let's imagine that we have the following program:
5456

55-
```rust
57+
```rust,ignore
5658
trait Debug { }
5759
5860
struct u32 { }
@@ -65,19 +67,22 @@ struct Vec<T> { }
6567
impl<T: Debug> Debug for Vec<T> { }
6668
```
6769

68-
Now imagine that we want to find answers for the query `exists<T> {
69-
Rc<T>: Debug }`. The first step would be to u-canonicalize this query; this
70-
is the act of giving canonical names to all the unbound inference variables based on the
71-
order of their left-most appearance, as well as canonicalizing the universes of any
72-
universally bound names (e.g., the `T` in `forall<T> { ... }`). In this case, there are no
73-
universally bound names, but the canonical form Q of the query might look something like:
70+
Now imagine that we want to find answers for the query `exists<T> { Rc<T>:
71+
Debug }`. The first step would be to u-canonicalize this query; this is the
72+
act of giving canonical names to all the unbound inference variables based on
73+
the order of their left-most appearance, as well as canonicalizing the
74+
universes of any universally bound names (e.g., the `T` in `forall<T> { ...
75+
}`). In this case, there are no universally bound names, but the canonical
76+
form Q of the query might look something like:
77+
78+
```text
79+
Rc<?0>: Debug
80+
```
7481

75-
Rc<?0>: Debug
76-
7782
where `?0` is a variable in the root universe U0. We would then go and
78-
look for a table with this as the key: since the forest is empty, this
79-
lookup will fail, and we will create a new table T0, corresponding to
80-
the u-canonical goal Q.
83+
look for a table with this canonical query as the key: since the forest is
84+
empty, this lookup will fail, and we will create a new table T0,
85+
corresponding to the u-canonical goal Q.
8186

8287
**Ignoring negative reasoning and regions.** To start, we'll ignore
8388
the possibility of negative goals like `not { Foo }`. We'll phase them
@@ -93,7 +98,7 @@ where-clauses that are in scope. In the case of our example, there
9398
would be three clauses, each coming from the program. Using a
9499
Prolog-like notation, these look like:
95100

96-
```
101+
```text
97102
(u32: Debug).
98103
(Rc<T>: Debug) :- (T: Debug).
99104
(Vec<T>: Debug) :- (T: Debug).
@@ -105,9 +110,9 @@ clauses are inapplicable because `u32` and `Vec<?0>` cannot be unified
105110
with `Rc<?0>`. The second clause, however, will work.
106111

107112
**What is a strand?** Let's talk a bit more about what a strand *is*. In the code, a strand
108-
is the combination of an inference table, an X-clause, and (possibly)
113+
is the combination of an inference table, an _X-clause_, and (possibly)
109114
a selected subgoal from that X-clause. But what is an X-clause
110-
(`ExClause`, in the code)? An X-clause pulls together a few things:
115+
([`ExClause`], in the code)? An X-clause pulls together a few things:
111116

112117
- The current state of the goal we are trying to prove;
113118
- A set of subgoals that have yet to be proven;
@@ -118,7 +123,9 @@ The general form of an X-clause is written much like a Prolog clause,
118123
but with somewhat different semantics. Since we're ignoring delayed
119124
literals and region constraints, an X-clause just looks like this:
120125

121-
G :- L
126+
```text
127+
G :- L
128+
```
122129

123130
where G is a goal and L is a set of subgoals that must be proven.
124131
(The L stands for *literal* -- when we address negative reasoning, a
@@ -128,7 +135,9 @@ that if we are able to prove L then the goal G can be considered true.
128135
In the case of our example, we would wind up creating one strand, with
129136
an X-clause like so:
130137

131-
(Rc<?T>: Debug) :- (?T: Debug)
138+
```text
139+
(Rc<?T>: Debug) :- (?T: Debug)
140+
```
132141

133142
Here, the `?T` refers to one of the inference variables created in the
134143
inference table that accompanies the strand. (I'll use named variables
@@ -141,37 +150,45 @@ is the subgoal after the turnstile (`:-`) that we are currently trying
141150
to prove in this strand. Initally, when a strand is first created,
142151
there is no selected subgoal.
143152

144-
**Activating a strand.** Now that we have created the table T0 and
145-
initialized it with strands, we have to actually try and produce an
146-
answer. We do this by invoking the `ensure_answer` operation on the
147-
table: specifically, we say `ensure_answer(T0, A0)`, meaning "ensure
148-
that there is a 0th answer".
149-
150-
Remember that tables store not only strands, but also a vector of
151-
cached answers. The first thing that `ensure_answer` does is to check
152-
whether answer 0 is in this vector. If so, we can just return
153-
immediately. In this case, the vector will be empty, and hence that
154-
does not apply (this becomes important for cyclic checks later on).
153+
[`ExClause`]: https://rust-lang-nursery.github.io/chalk/doc/chalk_engine/struct.ExClause.html
155154

156-
When there is no cached answer, `ensure_answer` will try to produce
157-
one. It does this by selecting a strand from the set of active
158-
strands -- the strands are stored in a `VecDeque` and hence processed
159-
in a round-robin fashion. Right now, we have only one strand, storing
160-
the following X-clause with no selected subgoal:
161-
162-
(Rc<?T>: Debug) :- (?T: Debug)
155+
**Activating a strand.** Now that we have created the table T0 and
156+
initialized it with strands, we have to actually try and produce an answer.
157+
We do this by invoking the [`ensure_root_answer`] operation on the table:
158+
specifically, we say `ensure_root_answer(T0, A0)`, meaning "ensure that there
159+
is a 0th answer A0 to query T0".
160+
161+
Remember that tables store not only strands, but also a vector of cached
162+
answers. The first thing that [`ensure_root_answer`] does is to check whether
163+
answer A0 is in this vector. If so, we can just return immediately. In this
164+
case, the vector will be empty, and hence that does not apply (this becomes
165+
important for cyclic checks later on).
166+
167+
When there is no cached answer, [`ensure_root_answer`] will try to produce one.
168+
It does this by selecting a strand from the set of active strands -- the
169+
strands are stored in a `VecDeque` and hence processed in a round-robin
170+
fashion. Right now, we have only one strand, storing the following X-clause
171+
with no selected subgoal:
172+
173+
```text
174+
(Rc<?T>: Debug) :- (?T: Debug)
175+
```
163176

164177
When we activate the strand, we see that we have no selected subgoal,
165178
and so we first pick one of the subgoals to process. Here, there is only
166179
one (`?T: Debug`), so that becomes the selected subgoal, changing
167180
the state of the strand to:
168181

169-
(Rc<?T>: Debug) :- selected(?T: Debug, A0)
182+
```text
183+
(Rc<?T>: Debug) :- selected(?T: Debug, A0)
184+
```
170185

171186
Here, we write `selected(L, An)` to indicate that (a) the literal `L`
172187
is the selected subgoal and (b) which answer `An` we are looking for. We
173188
start out looking for `A0`.
174189

190+
[`ensure_root_answer`]: https://rust-lang-nursery.github.io/chalk/doc/chalk_engine/forest/struct.Forest.html#method.ensure_root_answer
191+
175192
**Processing the selected subgoal.** Next, we have to try and find an
176193
answer to this selected goal. To do that, we will u-canonicalize it
177194
and try to find an associated table. In this case, the u-canonical
@@ -189,7 +206,7 @@ will be:
189206
We can thus summarize the state of the whole forest at this point as
190207
follows:
191208

192-
```
209+
```text
193210
Table T0 [Rc<?0>: Debug]
194211
Strands:
195212
(Rc<?T>: Debug) :- selected(?T: Debug, A0)
@@ -215,7 +232,7 @@ answer). The strand is then removed from the list of strands.
215232

216233
The state of table T1 is therefore:
217234

218-
```
235+
```text
219236
Table T1 [?0: Debug]
220237
Answers:
221238
A0 = [?0 = u32]
@@ -227,16 +244,18 @@ Table T1 [?0: Debug]
227244
Note that I am writing out the answer A0 as a substitution that can be
228245
applied to the table goal; actually, in the code, the goals for each
229246
X-clause are also represented as substitutions, but in this exposition
230-
I've chosen to write them as full goals, following NFTD.
231-
247+
I've chosen to write them as full goals, following [NFTD].
248+
249+
[NFTD]: ./bibliography.html#slg
250+
232251
Since we now have an answer, `ensure_answer(T1, A0)` will return `Ok`
233252
to the table T0, indicating that answer A0 is available. T0 now has
234253
the job of incorporating that result into its active strand. It does
235254
this in two ways. First, it creates a new strand that is looking for
236255
the next possible answer of T1. Next, it incorpoates the answer from
237256
A0 and removes the subgoal. The resulting state of table T0 is:
238257

239-
```
258+
```text
240259
Table T0 [Rc<?0>: Debug]
241260
Strands:
242261
(Rc<?T>: Debug) :- selected(?T: Debug, A1)
@@ -250,7 +269,7 @@ then be returned up to our caller, and the whole forest goes quiescent
250269
at this point (remember, we only do enough work to generate *one*
251270
answer). The ending state of the forest at this point will be:
252271

253-
```
272+
```text
254273
Table T0 [Rc<?0>: Debug]
255274
Answer:
256275
A0 = [?0 = u32]

0 commit comments

Comments
 (0)