Skip to content

Commit a0850aa

Browse files
committed
w
1 parent 43b956e commit a0850aa

File tree

1 file changed

+64
-7
lines changed

1 file changed

+64
-7
lines changed

src/solve/the-solver.md

+64-7
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,69 @@ approach.
66

77
[chalk]: https://rust-lang.github.io/chalk/book/recursive.html
88

9-
The basic structure of the solver is a pure function
10-
`fn evaluate_goal(goal: Goal<'tcx>) -> Response`.
11-
While the actual solver is not fully pure to deal with overflow and cycles, we are
12-
going to defer that for now.
9+
## A rough walkthrough
1310

14-
To deal with inference variables and to improve caching, we use
15-
[canonicalization](./canonicalization.md).
11+
The entry-point of the solver is `InferCtxtEvalExt::evaluate_root_goal`. This
12+
function sets up the root `EvalCtxt` and then calls `EvalCtxt::evaluate_goal`,
13+
to actually enter the trait solver.
1614

17-
TODO: write the remaining code for this as well.
15+
`EvalCtxt::evaluate_goal` handles [canonicalization](./canonicalization.md), caching,
16+
overflow, and solver cycles. Once that is done, it creates a nested `EvalCtxt` with a
17+
separate local `InferCtxt` and calls `EvalCtxt::compute_goal`, which is responsible for the
18+
'actual solver behavior'. We match on the `PredicateKind`, delegating to a separate function
19+
for each one.
20+
21+
For trait goals, such a `Vec<T>: Clone`, `EvalCtxt::compute_trait_goal` has
22+
to collect all the possible ways this goal can be proven via
23+
`EvalCtxt::assemble_and_evaluate_candidates`. Each candidate is handled in
24+
a separate "probe", to not leak inference constraints to the other candidates.
25+
We then try to merge the assembled candidates via `EvalCtxt::merge_candidates`.
26+
27+
28+
## Important concepts and design pattern
29+
30+
### `EvalCtxt::add_goal`
31+
32+
To prove nested goals, we don't directly call `EvalCtxt::compute_goal`, but instead
33+
add the goal to the `EvalCtxt` with `EvalCtxt::all_goal`. We then prove all nested
34+
goals together in either `EvalCtxt::try_evaluate_added_goals` or
35+
`EvalCtxt::evaluate_added_goals_and_make_canonical_response`. This allows us to handle
36+
inference constraints from later goals.
37+
38+
E.g. if we have both `?x: Debug` and `(): ConstrainToU8<?x>` as nested goals, then proving `?x: Debug` is initially ambiguous, but after proving `(): ConstrainToU8<?x>` we constrain
39+
`?x` to `u8` and proving `u8: Debug` succeeds.
40+
41+
### Matching on `TyKind`
42+
43+
We lazily normalize types in the solver, so we always have to assume that any types
44+
and constants are potentially unnormalized. This means that matching on `TyKind` can easily
45+
be incorrect.
46+
47+
We handle normalization in two different ways. When proving `Trait` goals when normalizing
48+
associated types, we separately assemble candidates depending on whether they structurally
49+
match the self type. Candidates which match on the self type are handled in
50+
`EvalCtxt::assemble_candidates_via_self_ty` which recurses via
51+
`EvalCtxt::assemble_candidates_after_normalizing_self_ty`, which normalizes the self type
52+
by one level. In all other cases we have to match on a `TyKind` we first use `EvalCtxt::try_normalize_ty` to normalize the type as much as possible.
53+
54+
### Higher ranked goals
55+
56+
In case the goal is higher-ranked, e.g. `for<'a> F: FnOnce(&'a ())`, `EvalCtxt::compute_goal`
57+
eagerly instantiates `'a` with a placeholder and then recursively proves
58+
`F: FnOnce(&'!a ())` as a nested goal.
59+
60+
### Dealing with choice
61+
62+
Some goals can be proven in multiple ways. In these cases we try each option in
63+
a separate "probe" and then attempt to merge the resulting responses by using
64+
`EvalCtxt::try_merge_responses`. If merging the responses fails, we use
65+
`EvalCtxt::flounder` instead, returning ambiguity. For some goals, we try
66+
incompletely prefer some choices over others in case `EvalCtxt::try_merge_responses`
67+
fails.
68+
69+
## Learning more
70+
71+
The solver should be fairly self-contained. I hope that the above information provides a
72+
good foundation when looking at the code itself. Please reach out on zulip if you get stuck
73+
while doing so or there are some quirks and design decisions which were unclear and deserve
74+
better comments or should be mentioned here.

0 commit comments

Comments
 (0)