@@ -9,7 +9,7 @@ deprecated once they become the standard kind of lifetime.)
99
1010The MIR-based region analysis consists of two major functions:
1111
12- - ` replace_regions_in_mir ` , invoked first, has two jobs:
12+ - [ ` replace_regions_in_mir ` ] , invoked first, has two jobs:
1313 - First, it finds the set of regions that appear within the
1414 signature of the function (e.g., ` 'a ` in `fn foo<'a>(&'a u32) {
1515 ... }`). These are called the "universal" or "free" regions – in
@@ -21,49 +21,67 @@ The MIR-based region analysis consists of two major functions:
2121 not of much interest. The intention is that – eventually – they
2222 will be "erased regions" (i.e., no information at all), since we
2323 won't be doing lexical region inference at all.
24- - ` compute_regions ` , invoked second: this is given as argument the
24+ - [ ` compute_regions ` ] , invoked second: this is given as argument the
2525 results of move analysis. It has the job of computing values for all
2626 the inference variables that ` replace_regions_in_mir ` introduced.
27- - To do that, it first runs the [ MIR type checker] ( #mirtypeck ) . This
27+ - To do that, it first runs the [ MIR type checker] . This
2828 is basically a normal type-checker but specialized to MIR, which
29- is much simpler than full Rust of course. Running the MIR type
29+ is much simpler than full Rust, of course. Running the MIR type
3030 checker will however create ** outlives constraints** between
3131 region variables (e.g., that one variable must outlive another
3232 one) to reflect the subtyping relationships that arise.
3333 - It also adds ** liveness constraints** that arise from where variables
3434 are used.
35- - More details to come, though the [ NLL RFC] also includes fairly thorough
36- (and hopefully readable) coverage.
35+ - After this, we create a [ ` RegionInferenceContext ` ] with the constraints we
36+ have computed and the inference variables we introduced and use the
37+ [ ` solve ` ] method to infer values for all region inference varaibles.
38+ - The [ NLL RFC] also includes fairly thorough (and hopefully readable)
39+ coverage.
3740
3841[ fvb ] : ../appendix/background.html#free-vs-bound
42+ [ `replace_regions_in_mir` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/fn.replace_regions_in_mir.html
43+ [ `compute_regions` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/fn.compute_regions.html
44+ [ `RegionInferenceContext` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html
45+ [ `solve` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#method.solve
3946[ NLL RFC ] : http://rust-lang.github.io/rfcs/2094-nll.html
47+ [ MIR type checker ] : ./type_check.md
4048
4149## Universal regions
4250
43- * to be written* – explain the ` UniversalRegions ` type
51+ The [ ` UnversalRegions ` ] type represents a collection of _ universal_ regions
52+ corresponding to some MIR ` DefId ` . It is constructed in
53+ [ ` replace_regions_in_mir ` ] when we replace all regions with fresh inference
54+ variables. [ ` UniversalRegions ` ] contains indices for all the free regions in
55+ the given MIR along with any relationships that are _ known_ to hold between
56+ them (e.g. implied bounds, where clauses, etc.).
4457
45- ## Region variables and constraints
58+ For example, given the MIR for the following function:
4659
47- * to be written* – describe the ` RegionInferenceContext ` and
48- the role of ` liveness_constraints ` vs other ` constraints ` , plus
49-
50- ## Closures
60+ ``` rust
61+ fn foo <'a >(x : & 'a u32 ) {
62+ // ...
63+ }
64+ ```
5165
52- * to be written*
66+ we would create a universal region for ` 'a ` and one for ` 'static ` . There may
67+ also be some complications for handling closures, but we will ignore those for
68+ the moment.
5369
54- < a name = " mirtypeck " ></ a >
70+ TODO: write about _ how _ these regions are computed.
5571
56- ## The MIR type-check
72+ [ `UniversalRegions` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/universal_regions/struct.UniversalRegions.html
5773
58- ## Representing the "values" of a region variable
74+ ## Region variables
5975
60- The value of a region can be thought of as a ** set** ; we call the
61- domain of this set a ` RegionElement ` . In the code, the value for all
62- regions is maintained in
63- [ the ` rustc_mir::borrow_check::nll::region_infer ` module] [ ri ] . For
64- each region we maintain a set storing what elements are present in its
65- value (to make this efficient, we give each kind of element an index,
66- the ` RegionElementIndex ` , and use sparse bitsets).
76+ The value of a region can be thought of as a ** set** . This set contains all
77+ points in the MIR where the region is valid along with any regions that are
78+ outlived by this region (e.g. if ` 'a: 'b ` , then ` end('b) ` is in the set for
79+ ` 'a ` ); we call the domain of this set a ` RegionElement ` . In the code, the value
80+ for all regions is maintained in [ the
81+ ` rustc_mir::borrow_check::nll::region_infer ` module] [ ri ] . For each region we
82+ maintain a set storing what elements are present in its value (to make this
83+ efficient, we give each kind of element an index, the ` RegionElementIndex ` , and
84+ use sparse bitsets).
6785
6886[ ri ] : https://github.com/rust-lang/rust/tree/master/src/librustc_mir/borrow_check/nll/region_infer/
6987
@@ -83,12 +101,148 @@ The kinds of region elements are as follows:
83101 for details on placeholders, see the section
84102 [ placeholders and universes] ( #placeholder ) .
85103
86- ## Causal tracking
104+ ## Constraints
105+
106+ Before we can infer the value of regions, we need to collect constraints on the
107+ regions. There are two primary types of constraints.
108+
109+ 1 . Outlives constraints. These are constraints that one region outlives another
110+ (e.g. ` 'a: 'b ` ). Outlives constraints are generated by the [ MIR type
111+ checker] .
112+ 2 . Liveness constraints. Each region needs to be live at points where it can be
113+ used. These constraints are collected by [ ` generate_constraints ` ] .
114+
115+ [ `generate_constraints` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/constraint_generation/fn.generate_constraints.html
116+
117+ ## Inference Overview
118+
119+ So how do we compute the contents of a region? This process is called _ region
120+ inference_ . The high-level idea is pretty simple, but there are some details we
121+ need to take care of.
122+
123+ Here is the high-level idea: we start off each region with the MIR locations we
124+ know must be in it from the liveness constraints. From there, we use all of the
125+ outlives constraints computed from the type checker to _ propagate_ the
126+ constraints: for each region ` 'a ` , if ` 'a: 'b ` , then we add all elements of
127+ ` 'b ` to ` 'a ` , including ` end('b) ` . This all happens in
128+ [ ` propagate_constraints ` ] .
129+
130+ Then, we will check for errors. We first check that type tests are satisfied by
131+ calling [ ` check_type_tests ` ] . This checks constraints like ` T: 'a ` . Second, we
132+ check that universal regions are not "too big". This is done by calling
133+ [ ` check_universal_regions ` ] . This checks that for each region ` 'a ` if ` 'a `
134+ contains the element ` end('b) ` , then we must already know that ` 'a: 'b ` holds
135+ (e.g. from a where clause). If we don't already know this, that is an error...
136+ well, almost. There is some special handling for closures that we will discuss
137+ later.
138+
139+ ### Example
140+
141+ Consider the following example:
87142
88- * to be written* – describe how we can extend the values of a variable
89- with causal tracking etc
143+ ``` rust,ignore
144+ fn foo<'a, 'b>(x: &'a usize) -> &'b usize {
145+ x
146+ }
147+ ```
148+
149+ Clearly, this should not compile because we don't know if ` 'a ` outlives ` 'b `
150+ (if it doesn't then the return value could be a dangling reference).
90151
91- <a name =" placeholder " ></a >
152+ Let's back up a bit. We need to introduce some free inference variables (as is
153+ done in [ ` replace_regions_in_mir ` ] ). This example doesn't use the exact regions
154+ produced, but it (hopefully) is enough to get the idea across.
155+
156+ ``` rust,ignore
157+ fn foo<'a, 'b>(x: &'a /* '#1 */ usize) -> &'b /* '#3 */ usize {
158+ x // '#2, location L1
159+ }
160+ ```
161+
162+ Some notation: ` '#1 ` , ` '#3 ` , and ` '#2 ` represent the universal regions for the
163+ argument, return value, and the expression ` x ` , respectively. Additionally, I
164+ will call the location of the expression ` x ` ` L1 ` .
165+
166+ So now we can use the liveness constraints to get the following starting points:
167+
168+ Region | Contents
169+ --------|----------
170+ '#1 |
171+ '#2 | ` L1 `
172+ '#3 | ` L1 `
173+
174+ Now we use the outlives constraints to expand each region. Specifically, we
175+ know that ` '#2: '#3 ` ...
176+
177+ Region | Contents
178+ --------|----------
179+ '#1 | ` L1 `
180+ '#2 | ` L1, end('#3) // add contents of '#3 and end('#3) `
181+ '#3 | ` L1 `
182+
183+ ... and ` '#1: '#2 ` , so ...
184+
185+ Region | Contents
186+ --------|----------
187+ '#1 | ` L1, end('#2), end('#3) // add contents of '#2 and end('#2) `
188+ '#2 | ` L1, end('#3) `
189+ '#3 | ` L1 `
190+
191+ Now, we need to check that no regions were too big (we don't have any type
192+ tests to check in this case). Notice that ` '#1 ` now contains ` end('#3) ` , but
193+ we have no ` where ` clause or implied bound to say that ` 'a: 'b ` ... that's an
194+ error!
195+
196+ ### Some details
197+
198+ The [ ` RegionInferenceContext ` ] type contains all of the information needed to
199+ do inference, including the universal regions from [ ` replace_regions_in_mir ` ] and
200+ the constraints computed for each region. It is constructed just after we
201+ compute the liveness constraints.
202+
203+ Here are some of the fields of the struct:
204+
205+ - [ ` constraints ` ] : contains all the outlives constraints.
206+ - [ ` liveness_constraints ` ] : contains all the liveness constraints.
207+ - [ ` universal_regions ` ] : contains the ` UniversalRegions ` returned by
208+ [ ` replace_regions_in_mir ` ] .
209+ - [ ` universal_region_relations ` ] : contains relations known to be true about
210+ universal regions. For example, if we have a where clause that ` 'a: 'b ` , that
211+ relation is assumed to be true while borrow checking the implementation (it
212+ is checked at the caller), so ` universal_region_relations ` would contain `'a:
213+ 'b`.
214+ - [ ` type_tests ` ] : contains some constraints on types that we must check after
215+ inference (e.g. ` T: 'a ` ).
216+ - [ ` closure_bounds_mapping ` ] : used for propagating region constraints from
217+ closures back out to the creater of the closure.
218+
219+ [ `constraints` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#structfield.constraints
220+ [ `liveness_constraints` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#structfield.liveness_constraints
221+ [ `universal_regions` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#structfield.universal_regions
222+ [ `universal_region_relations` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#structfield.universal_region_relations
223+ [ `type_tests` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#structfield.type_tests
224+ [ `closure_bounds_mapping` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#structfield.closure_bounds_mapping
225+
226+ TODO: should we discuss any of the others fields? What about the SCCs?
227+
228+ Ok, now that we have constructed a ` RegionInferenceContext ` , we can do
229+ inference. This is done by calling the [ ` solve ` ] method on the context. This
230+ is where we call [ ` propagate_constraints ` ] and then check the resulting type
231+ tests and universal regions, as discussed above.
232+
233+ [ `propagate_constraints` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#method.propagate_constraints
234+ [ `check_type_tests` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#method.check_type_tests
235+ [ `check_universal_regions` ] : https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/nll/region_infer/struct.RegionInferenceContext.html#method.check_universal_regions
236+
237+ ## Closures
238+
239+ When we are checking the type tests and universal regions, we may come across a
240+ constraint that we can't prove yet if we are in a closure body! However, the
241+ necessary constraints may actually hold (we just don't know it yet). Thus, if
242+ we are inside a closure, we just collect all the constraints we can't prove yet
243+ and return them. Later, when we are borrow check the MIR node that created the
244+ closure, we can also check that these constraints hold. At that time, if we
245+ can't prove they hold, we report an error.
92246
93247## Placeholders and universes
94248
@@ -534,3 +688,6 @@ Now constraint propagation is done, but when we check the outlives
534688relationships, we find that ` V2 ` includes this new element ` placeholder(1) ` ,
535689so we report an error.
536690
691+ ## Borrow Checker Errors
692+
693+ TODO: we should discuss how to generate errors from the results of these analyses.
0 commit comments