You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
0 commit comments