src/doc/rustc-dev-guide/src/borrow-check/region-inference.md MARKDOWN 235 lines View on github.com → Search inside
1# Region inference (NLL)23The MIR-based region checking code is located in [the `rustc_mir::borrow_check`4module][nll].56[nll]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/index.html78The MIR-based region analysis consists of two major functions:910- [`replace_regions_in_mir`], invoked first, has two jobs:11  - First, it finds the set of regions that appear within the12    signature of the function (e.g., `'a` in `fn foo<'a>(&'a u32) {13    ... }`). These are called the "universal" or "free" regions – in14    particular, they are the regions that [appear free][fvb] in the15    function body.16  - Second, it replaces all the regions from the function body with17    fresh inference variables. This is because (presently) those18    regions are the results of lexical region inference and hence are19    not of much interest. The intention is that  eventually  they20    will be "erased regions" (i.e., no information at all), since we21    won't be doing lexical region inference at all.22- [`compute_regions`], invoked second: this is given as argument the23  results of move analysis. It has the job of computing values for all24  the inference variables that `replace_regions_in_mir` introduced.25  - To do that, it first runs the [MIR type checker]. This is26    basically a normal type-checker but specialized to MIR, which is27    much simpler than full Rust, of course. Running the MIR type28    checker will however create various [constraints][cp] between region29    variables, indicating their potential values and relationships to30    one another.31  - After this, we perform [constraint propagation][cp] by creating a32    [`RegionInferenceContext`] and invoking its [`solve`]33    method.34  - The [NLL RFC] also includes fairly thorough (and hopefully readable)35    coverage.3637[cp]: ./region-inference/constraint-propagation.md38[fvb]: ../appendix/background.md#free-vs-bound39[`replace_regions_in_mir`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/nll/fn.replace_regions_in_mir.html40[`compute_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/nll/fn.compute_regions.html41[`RegionInferenceContext`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html42[`solve`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#method.solve43[NLL RFC]: https://rust-lang.github.io/rfcs/2094-nll.html44[MIR type checker]: ./type-check.md4546## Universal regions4748The [`UniversalRegions`] type represents a collection of _universal_ regions49corresponding to some MIR `DefId`. It is constructed in50[`replace_regions_in_mir`] when we replace all regions with fresh inference51variables. [`UniversalRegions`] contains indices for all the free regions in52the given MIR along with any relationships that are _known_ to hold between53them (e.g. implied bounds, where clauses, etc.).5455For example, given the MIR for the following function:5657```rust58fn foo<'a>(x: &'a u32) {59    // ...60}61```6263we would create a universal region for `'a` and one for `'static`. There may64also be some complications for handling closures, but we will ignore those for65the moment.6667TODO: write about _how_ these regions are computed.6869[`UniversalRegions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/universal_regions/struct.UniversalRegions.html7071<a id="region-variables"></a>7273## Region variables7475The value of a region can be thought of as a **set**. This set contains all76points in the MIR where the region is valid along with any regions that are77outlived by this region (e.g. if `'a: 'b`, then `end('b)` is in the set for78`'a`); we call the domain of this set a `RegionElement`. In the code, the value79for all regions is maintained in [the `rustc_borrowck::region_infer` module][ri].80For each region we maintain a set storing what elements are present in its value (to make this81efficient, we give each kind of element an index, the `RegionElementIndex`, and82use sparse bitsets).8384[ri]: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_borrowck/src/region_infer8586The kinds of region elements are as follows:8788- Each **[`location`]** in the MIR control-flow graph: a location is just89  the pair of a basic block and an index. This identifies the point90  **on entry** to the statement with that index (or the terminator, if91  the index is equal to `statements.len()`).92- There is an element `end('a)` for each universal region `'a`,93  corresponding to some portion of the caller's (or caller's caller,94  etc) control-flow graph.95- Similarly, there is an element denoted `end('static)` corresponding96  to the remainder of program execution after this function returns.97- There is an element `!1` for each placeholder region `!1`. This98  corresponds (intuitively) to some unknown set of other elements 99  for details on placeholders, see the section100  [placeholders and universes](region-inference/placeholders-and-universes.md).101102## Constraints103104Before we can infer the value of regions, we need to collect105constraints on the regions. The full set of constraints is described106in [the section on constraint propagation][cp], but the two most107common sorts of constraints are:1081091. Outlives constraints. These are constraints that one region outlives another110   (e.g. `'a: 'b`). Outlives constraints are generated by the [MIR type111   checker].1122. Liveness constraints. Each region needs to be live at points where it can be113   used.114115## Inference Overview116117So how do we compute the contents of a region? This process is called _region118inference_. The high-level idea is pretty simple, but there are some details we119need to take care of.120121Here is the high-level idea: we start off each region with the MIR locations we122know must be in it from the liveness constraints. From there, we use all of the123outlives constraints computed from the type checker to _propagate_ the124constraints: for each region `'a`, if `'a: 'b`, then we add all elements of125`'b` to `'a`, including `end('b)`. This all happens in126[`propagate_constraints`].127128Then, we will check for errors. We first check that type tests are satisfied by129calling [`check_type_tests`]. This checks constraints like `T: 'a`. Second, we130check that universal regions are not "too big". This is done by calling131[`check_universal_regions`]. This checks that for each region `'a` if `'a`132contains the element `end('b)`, then we must already know that `'a: 'b` holds133(e.g. from a where clause). If we don't already know this, that is an error...134well, almost. There is some special handling for closures that we will discuss135later.136137### Example138139Consider the following example:140141```rust,ignore142fn foo<'a, 'b>(x: &'a usize) -> &'b usize {143    x144}145```146147Clearly, this should not compile because we don't know if `'a` outlives `'b`148(if it doesn't then the return value could be a dangling reference).149150Let's back up a bit. We need to introduce some free inference variables (as is151done in [`replace_regions_in_mir`]). This example doesn't use the exact regions152produced, but it (hopefully) is enough to get the idea across.153154```rust,ignore155fn foo<'a, 'b>(x: &'a /* '#1 */ usize) -> &'b /* '#3 */ usize {156    x // '#2, location L1157}158```159160Some notation: `'#1`, `'#3`, and `'#2` represent the universal regions for the161argument, return value, and the expression `x`, respectively. Additionally, I162will call the location of the expression `x` `L1`.163164So now we can use the liveness constraints to get the following starting points:165166Region  | Contents167--------|----------168'#1     |169'#2     | `L1`170'#3     | `L1`171172Now we use the outlives constraints to expand each region. Specifically, we173know that `'#2: '#3` ...174175Region  | Contents176--------|----------177'#1     | `L1`178'#2     | `L1, end('#3) // add contents of '#3 and end('#3)`179'#3     | `L1`180181... and `'#1: '#2`, so ...182183Region  | Contents184--------|----------185'#1     | `L1, end('#2), end('#3) // add contents of '#2 and end('#2)`186'#2     | `L1, end('#3)`187'#3     | `L1`188189Now, we need to check that no regions were too big (we don't have any type190tests to check in this case). Notice that `'#1` now contains `end('#3)`, but191we have no `where` clause or implied bound to say that `'a: 'b`... that's an192error!193194### Some details195196The [`RegionInferenceContext`] type contains all of the information needed to197do inference, including the universal regions from [`replace_regions_in_mir`] and198the constraints computed for each region. It is constructed just after we199compute the liveness constraints.200201Here are some of the fields of the struct:202203- [`constraints`]: contains all the outlives constraints.204- [`liveness_constraints`]: contains all the liveness constraints.205- [`universal_regions`]: contains the `UniversalRegions` returned by206  [`replace_regions_in_mir`].207- [`universal_region_relations`]: contains relations known to be true about208  universal regions. For example, if we have a where clause that `'a: 'b`, that209  relation is assumed to be true while borrow checking the implementation (it210  is checked at the caller), so `universal_region_relations` would contain `'a:211  'b`.212- [`type_tests`]: contains some constraints on types that we must check after213  inference (e.g. `T: 'a`).214- [`closure_bounds_mapping`]: used for propagating region constraints from215  closures back out to the creator of the closure.216217[`constraints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#structfield.constraints218[`liveness_constraints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#structfield.liveness_constraints219[`location`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/struct.Location.html220[`universal_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#structfield.universal_regions221[`universal_region_relations`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#structfield.universal_region_relations222[`type_tests`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#structfield.type_tests223[`closure_bounds_mapping`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#structfield.closure_bounds_mapping224225TODO: should we discuss any of the others fields? What about the SCCs?226227Ok, now that we have constructed a `RegionInferenceContext`, we can do228inference. This is done by calling the [`solve`] method on the context. This229is where we call [`propagate_constraints`] and then check the resulting type230tests and universal regions, as discussed above.231232[`propagate_constraints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#method.propagate_constraints233[`check_type_tests`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#method.check_type_tests234[`check_universal_regions`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_borrowck/region_infer/struct.RegionInferenceContext.html#method.check_universal_regions

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.