Skip to content

Commit 66d43a8

Browse files
lcnrBoxyUwU
authored andcommitted
is this sensible? idk
1 parent a10a29a commit 66d43a8

File tree

1 file changed

+77
-38
lines changed

1 file changed

+77
-38
lines changed

src/solve/normalization.md

+77-38
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,80 @@
33
With the new solver we've made some fairly significant changes to normalization when compared
44
to the existing implementation.
55

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].
6+
We now differentiate between "one-step normalization", "structural normalization" and
7+
"deep normalization".
8+
9+
## One-step normalization
10+
11+
One-step normalization is implemented via `NormalizesTo` goals. Unlike other goals
12+
in the trait solver, `NormalizesTo` always expects the term to be an unconstrained
13+
inference variable[^opaques]. Think of it as a function, taking an alias as input
14+
and returning its underlying value. If the alias is rigid, `NormalizesTo` fails and
15+
returns `NoSolution`.
16+
17+
The underlying value may itself be an unnormalized alias, e.g.
18+
`NormalizesTo(<<() as Id>::This as Id>::This)` only returns `<() as Id>::This`,
19+
even though that alias can be further normalized to `()`. As the term is
20+
always an unconstrained inference variable, the expected term cannot influence
21+
normalization, see [trait-system-refactor-initiative#22] for more.
22+
23+
Only ever computing `NormalizesTo` goals with an unconstrained inference variable
24+
requires special solver support. It is only used by `AliasRelate` goals and pending
25+
`NormalizesTo` goals are tracked separately from other goals: [source][try-eval-norm].
26+
As the expected term is always erased in `NormalizesTo`, we have to return its
27+
ambiguous nested goals to its caller as not doing so weakens inference. See
28+
[#122687] for more details.
29+
30+
[trait-system-refactor-initiative#22]: https://github.com/rust-lang/trait-system-refactor-initiative/issues/22
31+
[try-eval-norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs#L523-L537
32+
[#122687]: https://github.com/rust-lang/rust/pull/122687
33+
34+
## `AliasRelate` and structural normalization
35+
36+
We structurally normalize an alias by applying one-step normalization until
37+
we end up with a rigid alias, ambiguity, or overflow. This is done by repeatedly
38+
evaluating `NormalizesTo` goals inside of a snapshot: [source][structural_norm].
39+
40+
`AliasRelate(lhs, rhs)` is implemented by first structurally normalizing both the
41+
`lhs` and the `rhs` and then relating the resulting rigid types (or inference
42+
variables). Importantly, if `lhs` or `rhs` ends up as an alias, this alias can
43+
now be treated as rigid and gets unified without emitting a nested `AliasRelate`
44+
goal: [source][structural-relate].
45+
46+
This means that `AliasRelate` with an unconstrained `rhs` ends up functioning
47+
similar to `NormalizesTo`, acting as a function which fully normalizes `lhs`
48+
before assigning the resulting rigid type to an inference variable. This is used by
49+
`fn structurally_normalize_ty` both [inside] and [outside] of the trait solver.
50+
This has to be used whenever we match on the value of some type, both inside
51+
and outside of the trait solver.
52+
53+
FIXME: structure, maybe we should have an "alias handling" chapter instead as
54+
talking about normalization without explaining that doesn't make too much
55+
sense.
56+
57+
FIXME: it is likely that this will subtly change again by mostly moving structural
58+
normalization into `NormalizesTo`.
59+
60+
[structural_norm]: https://github.com/rust-lang/rust/blob/2627e9f3012a97d3136b3e11bf6bd0853c38a534/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L140-L175
61+
[structural-relate]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/alias_relate.rs#L88-L107
62+
[inside]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/solve/mod.rs#L278-L299
63+
[outside]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_trait_selection/src/traits/structural_normalize.rs#L17-L48
64+
65+
## Deep normalization
66+
67+
By walking over a type, and using `fn structurally_normalize_ty` for each encountered
68+
alias, it is possible to deeply normalize a type, normalizing all aliases as much as
69+
possible. However, this only works for aliases referencing bound variables if they are
70+
not ambiguous as we're unable to replace the alias with a corresponding inference
71+
variable without leaking universes.
72+
73+
FIXME: we previously had to also be careful about instantiating the new inference
74+
variable with another normalizeable alias. Due to our recent changes to generalization,
75+
this should not be the case anymore. Equating an inference variable with an alias
76+
now always uses `AliasRelate` to fully normalize the alias before instantiating the
77+
inference variable: [source][generalize-no-alias]
78+
79+
[generalize-no-alias]: https://github.com/rust-lang/rust/blob/a0569fa8f91b5271e92d2f73fd252de7d3d05b9c/compiler/rustc_infer/src/infer/relate/generalize.rs#L353-L358
3280

3381
## Outside of the trait solver
3482

@@ -41,10 +89,8 @@ Luckily deep normalization is currently only necessary in places where there is
4189

4290
If we only care about the outermost layer of types, we instead use
4391
`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.
92+
Unlike `At::deeply_normalize`, structural normalization is also used in cases where we
93+
have to handle ambiguity.
4894

4995
Because this may result in behavior changes depending on how the trait solver handles
5096
ambiguity, it is safer to also require full normalization there. This happens in
@@ -70,11 +116,4 @@ for deep normalization to the new solver we cannot emulate this behavior. This d
70116
for projections with bound variables, sometimes leaving them unnormalized. An approach which
71117
also supports projections with bound variables will be even more involved.
72118

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.
119+
[^opaques]: opaque types are currently handled a bit differently. this may change in the future

0 commit comments

Comments
 (0)