11
11
use super :: RegionIndex ;
12
12
use super :: free_regions:: FreeRegions ;
13
13
use rustc:: infer:: InferCtxt ;
14
+ use rustc:: middle:: free_region:: FreeRegionMap ;
14
15
use rustc:: mir:: { Location , Mir } ;
15
16
use rustc:: ty;
16
17
use rustc_data_structures:: indexed_vec:: { Idx , IndexVec } ;
@@ -49,17 +50,15 @@ pub struct RegionInferenceContext<'tcx> {
49
50
50
51
/// The constraints we have accumulated and used during solving.
51
52
constraints : Vec < Constraint > ,
53
+
54
+ free_region_map : & ' tcx FreeRegionMap < ' tcx > ,
52
55
}
53
56
54
57
#[ derive( Default ) ]
55
58
struct RegionDefinition < ' tcx > {
56
59
/// If this is a free-region, then this is `Some(X)` where `X` is
57
60
/// the name of the region.
58
61
name : Option < ty:: Region < ' tcx > > ,
59
-
60
- /// If true, this is a constant region which cannot grow larger.
61
- /// This is used for named regions as well as `'static`.
62
- constant : bool ,
63
62
}
64
63
65
64
/// The value of an individual region variable. Region variables
@@ -113,7 +112,7 @@ pub struct Constraint {
113
112
point : Location ,
114
113
}
115
114
116
- impl < ' a , ' gcx , ' tcx > RegionInferenceContext < ' tcx > {
115
+ impl < ' tcx > RegionInferenceContext < ' tcx > {
117
116
/// Creates a new region inference context with a total of
118
117
/// `num_region_variables` valid inference variables; the first N
119
118
/// of those will be constant regions representing the free
@@ -131,6 +130,7 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
131
130
inferred_values : None ,
132
131
constraints : Vec :: new ( ) ,
133
132
free_regions : Vec :: new ( ) ,
133
+ free_region_map : free_regions. free_region_map ,
134
134
} ;
135
135
136
136
result. init_free_regions ( free_regions, mir) ;
@@ -158,9 +158,9 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
158
158
/// is just itself. R1 (`'b`) in contrast also outlives `'a` and
159
159
/// hence contains R0 and R1.
160
160
fn init_free_regions ( & mut self , free_regions : & FreeRegions < ' tcx > , mir : & Mir < ' tcx > ) {
161
- let & FreeRegions {
162
- ref indices,
163
- ref free_region_map,
161
+ let FreeRegions {
162
+ indices,
163
+ free_region_map : _ ,
164
164
} = free_regions;
165
165
166
166
// For each free region X:
@@ -171,7 +171,6 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
171
171
172
172
// Initialize the name and a few other details.
173
173
self . definitions [ variable] . name = Some ( free_region) ;
174
- self . definitions [ variable] . constant = true ;
175
174
176
175
// Add all nodes in the CFG to `definition.value`.
177
176
for ( block, block_data) in mir. basic_blocks ( ) . iter_enumerated ( ) {
@@ -188,13 +187,6 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
188
187
// Add `end(X)` into the set for X.
189
188
self . liveness_constraints [ variable] . add_free_region ( variable) ;
190
189
191
- // Go through each region Y that outlives X (i.e., where
192
- // Y: X is true). Add `end(X)` into the set for `Y`.
193
- for superregion in free_region_map. regions_that_outlive ( & free_region) {
194
- let superregion_index = RegionIndex :: new ( indices[ superregion] ) ;
195
- self . liveness_constraints [ superregion_index] . add_free_region ( variable) ;
196
- }
197
-
198
190
debug ! (
199
191
"init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`" ,
200
192
free_region,
@@ -253,84 +245,151 @@ impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> {
253
245
}
254
246
255
247
/// Perform region inference.
256
- pub ( super ) fn solve ( & mut self , infcx : & InferCtxt < ' a , ' gcx , ' tcx > , mir : & Mir < ' tcx > ) {
248
+ pub ( super ) fn solve ( & mut self , infcx : & InferCtxt < ' _ , ' _ , ' tcx > , mir : & Mir < ' tcx > ) {
257
249
assert ! ( self . inferred_values. is_none( ) , "values already inferred" ) ;
258
- let errors = self . propagate_constraints ( mir) ;
259
-
260
- // worst error msg ever
261
- for ( fr1, span, fr2) in errors {
262
- infcx. tcx . sess . span_err (
263
- span,
264
- & format ! (
265
- "free region `{}` does not outlive `{}`" ,
266
- self . definitions[ fr1] . name. unwrap( ) ,
267
- self . definitions[ fr2] . name. unwrap( )
268
- ) ,
269
- ) ;
250
+
251
+ // Find the minimal regions that can solve the constraints. This is infallible.
252
+ self . propagate_constraints ( mir) ;
253
+
254
+ // Now, see whether any of the constraints were too strong. In particular,
255
+ // we want to check for a case where a free region exceeded its bounds.
256
+ // Consider:
257
+ //
258
+ // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
259
+ //
260
+ // In this case, returning `x` requires `&'a u32 <: &'b u32`
261
+ // and hence we establish (transitively) a constraint that
262
+ // `'a: 'b`. The `propagate_constraints` code above will
263
+ // therefore add `end('a)` into the region for `'b` -- but we
264
+ // have no evidence that `'b` outlives `'a`, so we want to report
265
+ // an error.
266
+ for free_region in & self . free_regions {
267
+ self . check_free_region ( infcx, * free_region) ;
268
+ }
269
+ }
270
+
271
+ fn check_free_region ( & self , infcx : & InferCtxt < ' _ , ' _ , ' tcx > , fr : RegionIndex ) {
272
+ let inferred_values = self . inferred_values . as_ref ( ) . unwrap ( ) ;
273
+ let fr_definition = & self . definitions [ fr] ;
274
+ let fr_name = fr_definition. name . unwrap ( ) ;
275
+ let fr_value = & inferred_values[ fr] ;
276
+
277
+ // Find every region `o` such that `fr: o`
278
+ // (because `fr` includes `end(o)`).
279
+ for & outlived_fr in & fr_value. free_regions {
280
+ // `fr` includes `end(fr)`, that's not especially
281
+ // interesting.
282
+ if fr == outlived_fr {
283
+ continue ;
284
+ }
285
+
286
+ let outlived_fr_definition = & self . definitions [ outlived_fr] ;
287
+ let outlived_fr_name = outlived_fr_definition. name . unwrap ( ) ;
288
+
289
+ // Check that `o <= fr`. If not, report an error.
290
+ if !self . free_region_map
291
+ . sub_free_regions ( outlived_fr_name, fr_name)
292
+ {
293
+ // worst error msg ever
294
+ let blame_span = self . blame_span ( fr, outlived_fr) ;
295
+ infcx. tcx . sess . span_err (
296
+ blame_span,
297
+ & format ! (
298
+ "free region `{}` does not outlive `{}`" ,
299
+ fr_name,
300
+ outlived_fr_name
301
+ ) ,
302
+ ) ;
303
+ }
270
304
}
271
305
}
272
306
273
307
/// Propagate the region constraints: this will grow the values
274
308
/// for each region variable until all the constraints are
275
309
/// satisfied. Note that some values may grow **too** large to be
276
310
/// feasible, but we check this later.
277
- fn propagate_constraints ( & mut self , mir : & Mir < ' tcx > ) -> Vec < ( RegionIndex , Span , RegionIndex ) > {
311
+ fn propagate_constraints ( & mut self , mir : & Mir < ' tcx > ) {
278
312
let mut changed = true ;
279
313
let mut dfs = Dfs :: new ( mir) ;
280
- let mut error_regions = FxHashSet ( ) ;
281
- let mut errors = vec ! [ ] ;
282
314
283
315
// The initial values for each region are derived from the liveness
284
316
// constraints we have accumulated.
285
317
let mut inferred_values = self . liveness_constraints . clone ( ) ;
286
318
287
319
while changed {
288
320
changed = false ;
321
+ debug ! ( "propagate_constraints: --------------------" ) ;
289
322
for constraint in & self . constraints {
290
- debug ! ( "constraint: {:?}" , constraint) ;
323
+ debug ! ( "propagate_constraints: constraint={:?}" , constraint) ;
324
+
291
325
let sub = & inferred_values[ constraint. sub ] . clone ( ) ;
292
326
let sup_value = & mut inferred_values[ constraint. sup ] ;
293
327
294
- debug ! ( " sub (before): {:?}" , sub ) ;
295
- debug ! ( " sup (before): {:?}" , sup_value ) ;
328
+ // Grow the value as needed to accommodate the
329
+ // outlives constraint.
296
330
297
- if !self . definitions [ constraint. sup ] . constant {
298
- // If this is not a constant, then grow the value as needed to
299
- // accommodate the outlives constraint.
331
+ if dfs. copy ( sub, sup_value, constraint. point ) {
332
+ debug ! ( "propagate_constraints: sub={:?}" , sub) ;
333
+ debug ! ( "propagate_constraints: sup={:?}" , sup_value) ;
334
+ changed = true ;
335
+ }
336
+ }
337
+ debug ! ( "\n " ) ;
338
+ }
300
339
301
- if dfs. copy ( sub, sup_value, constraint. point ) {
302
- changed = true ;
303
- }
340
+ self . inferred_values = Some ( inferred_values) ;
341
+ }
304
342
305
- debug ! ( " sup (after) : {:?}" , sup_value) ;
306
- debug ! ( " changed : {:?}" , changed) ;
307
- } else {
308
- // If this is a constant, check whether it *would
309
- // have* to grow in order for the constraint to be
310
- // satisfied. If so, create an error.
311
-
312
- let mut sup_value = & mut sup_value. clone ( ) ;
313
- if dfs. copy ( sub, sup_value, constraint. point ) {
314
- // Constant values start out with the entire
315
- // CFG, so it must be some new free region
316
- // that was added. Find one.
317
- let & new_region = sup_value
318
- . free_regions
319
- . difference ( & sup_value. free_regions )
320
- . next ( )
321
- . unwrap ( ) ;
322
- debug ! ( " new_region : {:?}" , new_region) ;
323
- if error_regions. insert ( constraint. sup ) {
324
- errors. push ( ( constraint. sup , constraint. span , new_region) ) ;
325
- }
343
+ /// Tries to finds a good span to blame for the fact that `fr1`
344
+ /// contains `fr2`.
345
+ fn blame_span ( & self , fr1 : RegionIndex , fr2 : RegionIndex ) -> Span {
346
+ // Find everything that influenced final value of `fr`.
347
+ let influenced_fr1 = self . dependencies ( fr1) ;
348
+
349
+ // Try to find some outlives constraint `'X: fr2` where `'X`
350
+ // influenced `fr1`. Blame that.
351
+ //
352
+ // NB, this is a pretty bad choice most of the time. In
353
+ // particular, the connection between `'X` and `fr1` may not
354
+ // be obvious to the user -- not to mention the naive notion
355
+ // of dependencies, which doesn't account for the locations of
356
+ // contraints at all. But it will do for now.
357
+ for constraint in & self . constraints {
358
+ if constraint. sub == fr2 && influenced_fr1[ constraint. sup ] {
359
+ return constraint. span ;
360
+ }
361
+ }
362
+
363
+ bug ! (
364
+ "could not find any constraint to blame for {:?}: {:?}" ,
365
+ fr1,
366
+ fr2
367
+ ) ;
368
+ }
369
+
370
+ /// Finds all regions whose values `'a` may depend on in some way.
371
+ /// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
372
+ /// and `dependencies('b)` will be in the final set.
373
+ ///
374
+ /// Used during error reporting, extremely naive and inefficient.
375
+ fn dependencies ( & self , r0 : RegionIndex ) -> IndexVec < RegionIndex , bool > {
376
+ let mut result_set = IndexVec :: from_elem ( false , & self . definitions ) ;
377
+ let mut changed = true ;
378
+ result_set[ r0] = true ;
379
+
380
+ while changed {
381
+ changed = false ;
382
+ for constraint in & self . constraints {
383
+ if result_set[ constraint. sup ] {
384
+ if !result_set[ constraint. sub ] {
385
+ result_set[ constraint. sub ] = true ;
386
+ changed = true ;
326
387
}
327
388
}
328
389
}
329
- debug ! ( "\n " ) ;
330
390
}
331
391
332
- self . inferred_values = Some ( inferred_values) ;
333
- errors
392
+ result_set
334
393
}
335
394
}
336
395
0 commit comments