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.