Skip to content

Commit e89d011

Browse files
committed
Add links and mark-i-m's suggestions
1 parent 62c8d04 commit e89d011

File tree

1 file changed

+61
-42
lines changed

1 file changed

+61
-42
lines changed

src/traits/slg.md

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -16,37 +16,39 @@ 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
@@ -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+
```
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
@@ -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+
```
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+
```
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:
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:
161172

162-
(Rc<?T>: Debug) :- (?T: Debug)
173+
```
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+
```
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
@@ -227,8 +244,10 @@ 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

0 commit comments

Comments
 (0)