@@ -14,7 +14,7 @@ use rustc::infer::InferCtxt;
14
14
use rustc:: infer:: NLLRegionVariableOrigin ;
15
15
use rustc:: infer:: RegionVariableOrigin ;
16
16
use rustc:: infer:: SubregionOrigin ;
17
- use rustc:: infer:: region_constraints:: VarOrigins ;
17
+ use rustc:: infer:: region_constraints:: { GenericKind , VarOrigins } ;
18
18
use rustc:: mir:: { ClosureOutlivesRequirement , ClosureRegionRequirements , Location , Mir } ;
19
19
use rustc:: ty:: { self , RegionVid } ;
20
20
use rustc_data_structures:: indexed_vec:: IndexVec ;
@@ -24,7 +24,7 @@ use syntax_pos::Span;
24
24
25
25
mod annotation;
26
26
mod dfs;
27
- use self :: dfs:: CopyFromSourceToTarget ;
27
+ use self :: dfs:: { CopyFromSourceToTarget , TestTargetOutlivesSource } ;
28
28
mod dump_mir;
29
29
mod graphviz;
30
30
mod values;
@@ -53,6 +53,9 @@ pub struct RegionInferenceContext<'tcx> {
53
53
/// The constraints we have accumulated and used during solving.
54
54
constraints : Vec < Constraint > ,
55
55
56
+ /// Type constraints that we check after solving.
57
+ type_tests : Vec < TypeTest < ' tcx > > ,
58
+
56
59
/// Information about the universally quantified regions in scope
57
60
/// on this function and their (known) relations to one another.
58
61
universal_regions : UniversalRegions < ' tcx > ,
@@ -95,6 +98,90 @@ pub struct Constraint {
95
98
span : Span ,
96
99
}
97
100
101
+ /// A "type test" corresponds to an outlives constraint between a type
102
+ /// and a lifetime, like `T: 'x` or `<T as Foo>::Bar: 'x`. They are
103
+ /// translated from the `Verify` region constraints in the ordinary
104
+ /// inference context.
105
+ ///
106
+ /// These sorts of constraints are handled differently than ordinary
107
+ /// constraints, at least at present. During type checking, the
108
+ /// `InferCtxt::process_registered_region_obligations` method will
109
+ /// attempt to convert a type test like `T: 'x` into an ordinary
110
+ /// outlives constraint when possible (for example, `&'a T: 'b` will
111
+ /// be converted into `'a: 'b` and registered as a `Constraint`).
112
+ ///
113
+ /// In some cases, however, there are outlives relationships that are
114
+ /// not converted into a region constraint, but rather into one of
115
+ /// these "type tests". The distinction is that a type test does not
116
+ /// influence the inference result, but instead just examines the
117
+ /// values that we ultimately inferred for each region variable and
118
+ /// checks that they meet certain extra criteria. If not, an error
119
+ /// can be issued.
120
+ ///
121
+ /// One reason for this is that these type tests always boil down to a
122
+ /// check like `'a: 'x` where `'a` is a universally quantified region
123
+ /// -- and therefore not one whose value is really meant to be
124
+ /// *inferred*, precisely. Another reason is that these type tests can
125
+ /// involve *disjunction* -- that is, they can be satisfied in more
126
+ /// than one way.
127
+ ///
128
+ /// For more information about this translation, see
129
+ /// `InferCtxt::process_registered_region_obligations` and
130
+ /// `InferCtxt::type_must_outlive` in `rustc::infer::outlives`.
131
+ #[ derive( Clone , Debug ) ]
132
+ pub struct TypeTest < ' tcx > {
133
+ /// The type `T` that must outlive the region.
134
+ pub generic_kind : GenericKind < ' tcx > ,
135
+
136
+ /// The region `'x` that the type must outlive.
137
+ pub lower_bound : RegionVid ,
138
+
139
+ /// The point where the outlives relation must hold.
140
+ pub point : Location ,
141
+
142
+ /// Where did this constraint arise?
143
+ pub span : Span ,
144
+
145
+ /// A test which, if met by the region `'x`, proves that this type
146
+ /// constraint is satisfied.
147
+ pub test : RegionTest ,
148
+ }
149
+
150
+ /// A "test" that can be applied to some "subject region" `'x`. These are used to
151
+ /// describe type constraints. Tests do not presently affect the
152
+ /// region values that get inferred for each variable; they only
153
+ /// examine the results *after* inference. This means they can
154
+ /// conveniently include disjuction ("a or b must be true").
155
+ #[ derive( Clone , Debug ) ]
156
+ pub enum RegionTest {
157
+ /// The subject region `'x` must by outlived by *some* region in
158
+ /// the given set of regions.
159
+ ///
160
+ /// This test comes from e.g. a where clause like `T: 'a + 'b`,
161
+ /// which implies that we know that `T: 'a` and that `T:
162
+ /// 'b`. Therefore, if we are trying to prove that `T: 'x`, we can
163
+ /// do so by showing that `'a: 'x` *or* `'b: 'x`.
164
+ IsOutlivedByAnyRegionIn ( Vec < RegionVid > ) ,
165
+
166
+ /// The subject region `'x` must by outlived by *all* regions in
167
+ /// the given set of regions.
168
+ ///
169
+ /// This test comes from e.g. a projection type like `T = <u32 as
170
+ /// Trait<'a, 'b>>::Foo`, which must outlive `'a` or `'b`, and
171
+ /// maybe both. Therefore we can prove that `T: 'x` if we know
172
+ /// that `'a: 'x` *and* `'b: 'x`.
173
+ IsOutlivedByAllRegionsIn ( Vec < RegionVid > ) ,
174
+
175
+ /// Any of the given tests are true.
176
+ ///
177
+ /// This arises from projections, for which there are multiple
178
+ /// ways to prove an outlives relationship.
179
+ Any ( Vec < RegionTest > ) ,
180
+
181
+ /// All of the given tests are true.
182
+ All ( Vec < RegionTest > ) ,
183
+ }
184
+
98
185
impl < ' tcx > RegionInferenceContext < ' tcx > {
99
186
/// Creates a new region inference context with a total of
100
187
/// `num_region_variables` valid inference variables; the first N
@@ -122,6 +209,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
122
209
liveness_constraints : RegionValues :: new ( elements, num_region_variables) ,
123
210
inferred_values : None ,
124
211
constraints : Vec :: new ( ) ,
212
+ type_tests : Vec :: new ( ) ,
125
213
universal_regions,
126
214
} ;
127
215
@@ -243,7 +331,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
243
331
} ) ;
244
332
}
245
333
246
- /// Perform region inference.
334
+ /// Add a "type test" that must be satisfied.
335
+ pub ( super ) fn add_type_test ( & mut self , type_test : TypeTest < ' tcx > ) {
336
+ self . type_tests . push ( type_test) ;
337
+ }
338
+
339
+ /// Perform region inference and report errors if we see any
340
+ /// unsatisfiable constraints. If this is a closure, returns the
341
+ /// region requirements to propagate to our creator, if any.
247
342
pub ( super ) fn solve (
248
343
& mut self ,
249
344
infcx : & InferCtxt < ' _ , ' _ , ' tcx > ,
@@ -254,6 +349,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
254
349
255
350
self . propagate_constraints ( mir) ;
256
351
352
+ self . check_type_tests ( infcx, mir) ;
353
+
257
354
let outlives_requirements = self . check_universal_regions ( infcx, mir_def_id) ;
258
355
259
356
if outlives_requirements. is_empty ( ) {
@@ -315,6 +412,123 @@ impl<'tcx> RegionInferenceContext<'tcx> {
315
412
self . inferred_values = Some ( inferred_values) ;
316
413
}
317
414
415
+ /// Once regions have been propagated, this method is used to see
416
+ /// whether any of the constraints were too strong. In particular,
417
+ /// we want to check for a case where a universally quantified
418
+ /// region exceeded its bounds. Consider:
419
+ ///
420
+ /// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
421
+ ///
422
+ /// In this case, returning `x` requires `&'a u32 <: &'b u32`
423
+ /// and hence we establish (transitively) a constraint that
424
+ /// `'a: 'b`. The `propagate_constraints` code above will
425
+ /// therefore add `end('a)` into the region for `'b` -- but we
426
+ /// have no evidence that `'b` outlives `'a`, so we want to report
427
+ /// an error.
428
+ fn check_type_tests (
429
+ & self ,
430
+ infcx : & InferCtxt < ' _ , ' _ , ' tcx > ,
431
+ mir : & Mir < ' tcx > ,
432
+ ) {
433
+ for type_test in & self . type_tests {
434
+ debug ! ( "check_type_test: {:?}" , type_test) ;
435
+
436
+ if !self . eval_region_test (
437
+ mir,
438
+ type_test. point ,
439
+ type_test. lower_bound ,
440
+ & type_test. test ,
441
+ ) {
442
+ // Oh the humanity. Obviously we will do better than this error eventually.
443
+ infcx. tcx . sess . span_err (
444
+ type_test. span ,
445
+ & format ! ( "failed type test: {:?}" , type_test) ,
446
+ ) ;
447
+ }
448
+ }
449
+ }
450
+
451
+ /// Test if `test` is true when applied to `lower_bound` at
452
+ /// `point`, and returns true or false.
453
+ fn eval_region_test (
454
+ & self ,
455
+ mir : & Mir < ' tcx > ,
456
+ point : Location ,
457
+ lower_bound : RegionVid ,
458
+ test : & RegionTest ,
459
+ ) -> bool {
460
+ debug ! (
461
+ "eval_region_test(point={:?}, lower_bound={:?}, test={:?})" ,
462
+ point,
463
+ lower_bound,
464
+ test
465
+ ) ;
466
+
467
+ match test {
468
+ RegionTest :: IsOutlivedByAllRegionsIn ( regions) => regions
469
+ . iter ( )
470
+ . all ( |& r| self . eval_outlives ( mir, r, lower_bound, point) ) ,
471
+
472
+ RegionTest :: IsOutlivedByAnyRegionIn ( regions) => regions
473
+ . iter ( )
474
+ . any ( |& r| self . eval_outlives ( mir, r, lower_bound, point) ) ,
475
+
476
+ RegionTest :: Any ( tests) => tests
477
+ . iter ( )
478
+ . any ( |test| self . eval_region_test ( mir, point, lower_bound, test) ) ,
479
+
480
+ RegionTest :: All ( tests) => tests
481
+ . iter ( )
482
+ . all ( |test| self . eval_region_test ( mir, point, lower_bound, test) ) ,
483
+ }
484
+ }
485
+
486
+ // Evaluate whether `sup_region: sub_region @ point`.
487
+ fn eval_outlives (
488
+ & self ,
489
+ mir : & Mir < ' tcx > ,
490
+ sup_region : RegionVid ,
491
+ sub_region : RegionVid ,
492
+ point : Location ,
493
+ ) -> bool {
494
+ debug ! (
495
+ "eval_outlives({:?}: {:?} @ {:?})" ,
496
+ sup_region,
497
+ sub_region,
498
+ point
499
+ ) ;
500
+
501
+ // Roughly speaking, do a DFS of all region elements reachable
502
+ // from `point` contained in `sub_region`. If any of those are
503
+ // *not* present in `sup_region`, the DFS will abort early and
504
+ // yield an `Err` result.
505
+ match self . dfs (
506
+ mir,
507
+ TestTargetOutlivesSource {
508
+ source_region : sub_region,
509
+ target_region : sup_region,
510
+ constraint_point : point,
511
+ elements : & self . elements ,
512
+ universal_regions : & self . universal_regions ,
513
+ inferred_values : self . inferred_values . as_ref ( ) . unwrap ( ) ,
514
+ } ,
515
+ ) {
516
+ Ok ( _) => {
517
+ debug ! ( "eval_outlives: true" ) ;
518
+ true
519
+ }
520
+
521
+ Err ( elem) => {
522
+ debug ! (
523
+ "eval_outlives: false because `{:?}` is not present in `{:?}`" ,
524
+ self . elements. to_element( elem) ,
525
+ sup_region
526
+ ) ;
527
+ false
528
+ }
529
+ }
530
+ }
531
+
318
532
/// Once regions have been propagated, this method is used to see
319
533
/// whether any of the constraints were too strong. In particular,
320
534
/// we want to check for a case where a universally quantified
0 commit comments