src/doc/rustc-dev-guide/src/solve/canonicalization.md MARKDOWN 83 lines View on github.com → Search inside
1# Canonicalization23Canonicalization is the process of *isolating* a value from its context and is necessary4for global caching of goals which include inference variables.56The idea is that given the goals `u32: Trait<?x>` and `u32: Trait<?y>`, where `?x` and `?y`7are two different currently unconstrained inference variables, we should get the same result8for both goals. We can therefore prove *the canonical query* `exists<T> u32: Trait<T>` once9and reuse the result.1011Let's first go over the way canonical queries work and then dive into the specifics of12how canonicalization works.1314## A walkthrough of canonical queries1516To make this a bit easier, let's use the trait goal `u32: Trait<?x>` as an example with the17assumption that the only relevant impl is `impl<T> Trait<Vec<T>> for u32`.1819### Canonicalizing the input2021We start by *canonicalizing* the goal, replacing inference variables with existential and22placeholders with universal bound variables. This would result in the *canonical goal*23`exists<T> u32: Trait<T>`.2425We remember the original values of all bound variables in the original context. Here this would26map `T` back to `?x`. These original values are used later on when dealing with the query27response.2829We now call the canonical query with the canonical goal.3031### Instantiating the canonical goal inside of the query3233To actually try to prove the canonical goal we start by instantiating the bound variables with34inference variables and placeholders again.3536This happens inside of the query in a completely separate `InferCtxt`. Inside of the query we37now have a goal `u32: Trait<?0>`. We also remember which value we've used to instantiate the bound38variables in the canonical goal, which maps `T` to `?0`.3940We now compute the goal `u32: Trait<?0>` and figure out that this holds, but we've constrained41`?0` to `Vec<?1>`. We finally convert this result to something useful to the caller.4243### Canonicalizing the query response4445We have to return to the caller both whether the goal holds, and the inference constraints46from inside of the query.4748To return the inference results to the caller we canonicalize the mapping from bound variables49to the instantiated values in the query. This means that the query response is `Certainty::Yes`50and a mapping from `T` to `exists<U> Vec<U>`.5152### Instantiating the query response5354The caller now has to apply the constraints returned by the query. For this they first55instantiate the bound variables of the canonical response with inference variables and56placeholders again, so the mapping in the response is now from `T` to `Vec<?z>`.5758It now equates the original value of `T` (`?x`) with the value for `T` in the59response (`Vec<?z>`), which correctly constrains `?x` to `Vec<?z>`.6061## `ExternalConstraints`6263Computing a trait goal may not only constrain inference variables, it can also add region64obligations, e.g. given a goal `(): AOutlivesB<'a, 'b>` we would like to return the fact that65`'a: 'b` has to hold.6667This is done by not only returning the mapping from bound variables to the instantiated values68from the query but also extracting additional `ExternalConstraints` from the `InferCtxt` context69while building the response.7071## How exactly does canonicalization work7273TODO: link to code once the PR lands and elaborate7475- types and consts: infer to existentially bound var, placeholder to universally bound var,76    considering universes77- generic parameters in the input get treated as placeholders in the root universe78- all regions in the input get all mapped to existentially bound vars and we "uniquify" them.79    `&'a (): Trait<'a>` gets canonicalized to `exists<'0, '1> &'0 (): Trait<'1>`. We do not care80    about their universes and simply put all regions into the highest universe of the input.81- in the output everything in a universe of the caller gets put into the root universe and only82    gets its correct universe when we unify the var values with the orig values of the caller83- we do not uniquify regions in the response and don't canonicalize `'static`

Findings

✓ No findings reported for this file.

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.