Skip to content

Commit 1726dad

Browse files
lcnrBoxyUwU
authored andcommitted
add section for normalization with the new solver
1 parent 492a6a8 commit 1726dad

File tree

2 files changed

+81
-0
lines changed

2 files changed

+81
-0
lines changed

src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@
124124
- [Canonicalization](./solve/canonicalization.md)
125125
- [Coinduction](./solve/coinduction.md)
126126
- [Proof trees](./solve/proof-trees.md)
127+
- [Normalization](./solve/normalization.md)
127128
- [Type checking](./type-checking.md)
128129
- [Method Lookup](./method-lookup.md)
129130
- [Variance](./variance.md)

src/solve/normalization.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Normalization in the new solver
2+
3+
With the new solver we've made some fairly significant changes to normalization when compared
4+
to the existing implementation.
5+
6+
We now differentiate between "shallow normalization" and "deep normalization".
7+
"Shallow normalization" normalizes a type until it is no-longer a potentially normalizeable alias;
8+
it does not recurse into the type. "deep normalization" replaces all normalizeable aliases in a
9+
type with its underlying type.
10+
11+
The old trait solver currently always deeply normalizes via `Projection` obligations.
12+
This is the only way to normalize in the old solver. By replacing projections with a new
13+
inference variable and then emitting `Projection(<T as Trait>::Assoc, ?new_infer)` the old
14+
solver successfully deeply normalizes even in the case of ambiguity. This approach does not
15+
work for projections referencing bound variables.
16+
17+
## Inside of the trait solver
18+
19+
Normalization in the new solver exclusively happens via `Projection`[^0] goals.
20+
This only succeeds by first normalizing the alias by one level and then equating
21+
it with the expected type. This differs from [the behavior of projection clauses]
22+
which can also be proven by successfully equating the projection without normalizating.
23+
This means that `Projection`[^0] goals must only be used in places where we
24+
*have to normalize* to make progress. To normalize `<T as Trait>::Assoc`, we first create
25+
a fresh inference variable `?normalized` and then prove
26+
`Projection(<T as Trait>::Assoc, ?normalized)`[^0]. `?normalized` is then constrained to
27+
the underlying type.
28+
29+
Inside of the trait solver we never deeply normalize. we only apply shallow normalization
30+
in [`assemble_candidates_after_normalizing_self_ty`] and inside for [`AliasRelate`]
31+
goals for the [`normalizes-to` candidates].
32+
33+
## Outside of the trait solver
34+
35+
The core type system - relating types and trait solving - will not need deep
36+
normalization with the new solver. There are still some areas which depend on it.
37+
For these areas there is the function `At::deeply_normalize`. Without additional
38+
trait solver support deep normalization does not always work in case of ambiguity.
39+
Luckily deep normalization is currently only necessary in places where there is no ambiguity.
40+
`At::deeply_normalize` immediately fails if there's ambiguity.
41+
42+
If we only care about the outermost layer of types, we instead use
43+
`At::structurally_normalize` or `FnCtxt::(try_)structurally_resolve_type`.
44+
Unlike `At::deeply_normalize`, shallow normalization is also used in cases where we
45+
have to handle ambiguity. `At::structurally_normalize` normalizes until the self type
46+
is either rigid or an inference variable and we're stuck with ambiguity. This means
47+
that the self type may not be fully normalized after `At::structurally_normalize` was called.
48+
49+
Because this may result in behavior changes depending on how the trait solver handles
50+
ambiguity, it is safer to also require full normalization there. This happens in
51+
`FnCtxt::structurally_resolve_type` which always emits a hard error if the self type ends
52+
up as an inference variable. There are some existing places which have a fallback for
53+
inference variables instead. These places use `try_structurally_resolve_type` instead.
54+
55+
## Why deep normalization with ambiguity is hard
56+
57+
Fully correct deep normalization is very challenging, especially with the new solver
58+
given that we do not want to deeply normalize inside of the solver. Mostly deeply normalizing
59+
but sometimes failing to do so is bound to cause very hard to minimize and understand bugs.
60+
If possible, avoiding any reliance on deep normalization entirely therefore feels preferable.
61+
62+
If the solver itself does not deeply normalize, any inference constraints returned by the
63+
solver would require normalization. Handling this correctly is ugly. This also means that
64+
we change goals we provide to the trait solver by "normalizing away" some projections.
65+
66+
The way we (mostly) guarantee deep normalization with the old solver is by eagerly replacing
67+
the projection with an inference variable and emitting a nested `Projection` goal. This works
68+
as `Projection` goals in the old solver deeply normalize. Unless we add another `PredicateKind`
69+
for deep normalization to the new solver we cannot emulate this behavior. This does not work
70+
for projections with bound variables, sometimes leaving them unnormalized. An approach which
71+
also supports projections with bound variables will be even more involved.
72+
73+
74+
[`assemble_candidates_after_normalizing_self_ty`]: https://github.com/rust-lang/rust/blob/1b6d4cdc4d923c148198ad4df230af48cdaca59e/compiler/rustc_trait_selection/src/solve/assembly/mod.rs#L330-L378
75+
[`AliasRelate`]: https://github.com/rust-lang/rust/blob/1b6d4cdc4d923c148198ad4df230af48cdaca59e/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L16-L102
76+
[`normalizes-to` candidates]: https://github.com/rust-lang/rust/blob/1b6d4cdc4d923c148198ad4df230af48cdaca59e/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L105-L151
77+
[the behavior of projection clauses]: https://github.com/rust-lang/trait-system-refactor-initiative/issues/1
78+
[normalize-via-infer]: https://github.com/rust-lang/rust/blob/1b6d4cdc4d923c148198ad4df230af48cdaca59e/compiler/rustc_trait_selection/src/solve/assembly/mod.rs#L350-L358
79+
80+
[^0]: TODO: currently refactoring this to use `NormalizesTo` predicates instead.

0 commit comments

Comments
 (0)