@@ -9,7 +9,7 @@ deprecated once they become the standard kind of lifetime.)
9
9
10
10
The MIR-based region analysis consists of two major functions:
11
11
12
- - ` replace_regions_in_mir ` , invoked first, has two jobs:
12
+ - [ ` replace_regions_in_mir ` ] , invoked first, has two jobs:
13
13
- First, it finds the set of regions that appear within the
14
14
signature of the function (e.g., ` 'a ` in `fn foo<'a>(&'a u32) {
15
15
... }`). These are called the "universal" or "free" regions – in
@@ -21,49 +21,67 @@ The MIR-based region analysis consists of two major functions:
21
21
not of much interest. The intention is that – eventually – they
22
22
will be "erased regions" (i.e., no information at all), since we
23
23
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
25
25
results of move analysis. It has the job of computing values for all
26
26
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
28
28
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
30
30
checker will however create ** outlives constraints** between
31
31
region variables (e.g., that one variable must outlive another
32
32
one) to reflect the subtyping relationships that arise.
33
33
- It also adds ** liveness constraints** that arise from where variables
34
34
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.
37
40
38
41
[ 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
39
46
[ NLL RFC ] : http://rust-lang.github.io/rfcs/2094-nll.html
47
+ [ MIR type checker ] : ./type_check.md
40
48
41
49
## Universal regions
42
50
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.).
44
57
45
- ## Region variables and constraints
58
+ For example, given the MIR for the following function:
46
59
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
+ ```
51
65
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.
53
69
54
- < a name = " mirtypeck " ></ a >
70
+ TODO: write about _ how _ these regions are computed.
55
71
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
57
73
58
- ## Representing the "values" of a region variable
74
+ ## Region variables
59
75
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).
67
85
68
86
[ ri ] : https://github.com/rust-lang/rust/tree/master/src/librustc_mir/borrow_check/nll/region_infer/
69
87
@@ -83,12 +101,148 @@ The kinds of region elements are as follows:
83
101
for details on placeholders, see the section
84
102
[ placeholders and universes] ( #placeholder ) .
85
103
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:
87
142
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).
90
151
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.
92
246
93
247
## Placeholders and universes
94
248
@@ -534,3 +688,6 @@ Now constraint propagation is done, but when we check the outlives
534
688
relationships, we find that ` V2 ` includes this new element ` placeholder(1) ` ,
535
689
so we report an error.
536
690
691
+ ## Borrow Checker Errors
692
+
693
+ TODO: we should discuss how to generate errors from the results of these analyses.
0 commit comments