Skip to content

Commit cd564d2

Browse files
committed
only propagate ClosureRegionRequirements if non-trivial
Before, we would always have a `Some` ClosureRegionRequirements if we were inferring values for a closure. Now we only do is it has a non-empty set of outlives requirements.
1 parent a0f0392 commit cd564d2

9 files changed

+148
-99
lines changed

src/librustc_mir/borrow_check/nll/region_infer/mod.rs

Lines changed: 92 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -251,24 +251,88 @@ impl<'tcx> RegionInferenceContext<'tcx> {
251251
mir_def_id: DefId,
252252
) -> Option<ClosureRegionRequirements> {
253253
assert!(self.inferred_values.is_none(), "values already inferred");
254-
let tcx = infcx.tcx;
255254

256-
// Find the minimal regions that can solve the constraints. This is infallible.
257255
self.propagate_constraints(mir);
258256

259-
// Now, see whether any of the constraints were too strong. In
260-
// particular, we want to check for a case where a universally
261-
// quantified region exceeded its bounds. Consider:
262-
//
263-
// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
264-
//
265-
// In this case, returning `x` requires `&'a u32 <: &'b u32`
266-
// and hence we establish (transitively) a constraint that
267-
// `'a: 'b`. The `propagate_constraints` code above will
268-
// therefore add `end('a)` into the region for `'b` -- but we
269-
// have no evidence that `'a` outlives `'b`, so we want to report
270-
// an error.
257+
let outlives_requirements = self.check_universal_regions(infcx, mir_def_id);
258+
259+
if outlives_requirements.is_empty() {
260+
None
261+
} else {
262+
let num_external_vids = self.universal_regions.num_global_and_external_regions();
263+
Some(ClosureRegionRequirements {
264+
num_external_vids,
265+
outlives_requirements,
266+
})
267+
}
268+
}
271269

270+
/// Propagate the region constraints: this will grow the values
271+
/// for each region variable until all the constraints are
272+
/// satisfied. Note that some values may grow **too** large to be
273+
/// feasible, but we check this later.
274+
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
275+
let mut changed = true;
276+
277+
debug!("propagate_constraints()");
278+
debug!("propagate_constraints: constraints={:#?}", {
279+
let mut constraints: Vec<_> = self.constraints.iter().collect();
280+
constraints.sort();
281+
constraints
282+
});
283+
284+
// The initial values for each region are derived from the liveness
285+
// constraints we have accumulated.
286+
let mut inferred_values = self.liveness_constraints.clone();
287+
288+
while changed {
289+
changed = false;
290+
debug!("propagate_constraints: --------------------");
291+
for constraint in &self.constraints {
292+
debug!("propagate_constraints: constraint={:?}", constraint);
293+
294+
// Grow the value as needed to accommodate the
295+
// outlives constraint.
296+
let Ok(made_changes) = self.dfs(
297+
mir,
298+
CopyFromSourceToTarget {
299+
source_region: constraint.sub,
300+
target_region: constraint.sup,
301+
inferred_values: &mut inferred_values,
302+
constraint_point: constraint.point,
303+
},
304+
);
305+
306+
if made_changes {
307+
debug!("propagate_constraints: sub={:?}", constraint.sub);
308+
debug!("propagate_constraints: sup={:?}", constraint.sup);
309+
changed = true;
310+
}
311+
}
312+
debug!("\n");
313+
}
314+
315+
self.inferred_values = Some(inferred_values);
316+
}
317+
318+
/// Once regions have been propagated, this method is used to see
319+
/// whether any of the constraints were too strong. In particular,
320+
/// we want to check for a case where a universally quantified
321+
/// region exceeded its bounds. Consider:
322+
///
323+
/// fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
324+
///
325+
/// In this case, returning `x` requires `&'a u32 <: &'b u32`
326+
/// and hence we establish (transitively) a constraint that
327+
/// `'a: 'b`. The `propagate_constraints` code above will
328+
/// therefore add `end('a)` into the region for `'b` -- but we
329+
/// have no evidence that `'b` outlives `'a`, so we want to report
330+
/// an error.
331+
fn check_universal_regions(
332+
&self,
333+
infcx: &InferCtxt<'_, '_, 'tcx>,
334+
mir_def_id: DefId,
335+
) -> Vec<ClosureOutlivesRequirement> {
272336
// The universal regions are always found in a prefix of the
273337
// full list.
274338
let universal_definitions = self.definitions
@@ -283,27 +347,23 @@ impl<'tcx> RegionInferenceContext<'tcx> {
283347
self.check_universal_region(infcx, fr, &mut outlives_requirements);
284348
}
285349

286-
// If this is not a closure, then there is no caller to which we can
287-
// "pass the buck". So if there are any outlives-requirements that were
288-
// not satisfied, we just have to report a hard error here.
289-
if !tcx.is_closure(mir_def_id) {
290-
for outlives_requirement in outlives_requirements {
291-
self.report_error(
292-
infcx,
293-
outlives_requirement.free_region,
294-
outlives_requirement.outlived_free_region,
295-
outlives_requirement.blame_span,
296-
);
297-
}
298-
return None;
350+
// If this is a closure, we can propagate unsatisfied
351+
// `outlives_requirements` to our creator. Otherwise, we have
352+
// to report a hard error here.
353+
if infcx.tcx.is_closure(mir_def_id) {
354+
return outlives_requirements;
299355
}
300356

301-
let num_external_vids = self.universal_regions.num_global_and_external_regions();
357+
for outlives_requirement in outlives_requirements {
358+
self.report_error(
359+
infcx,
360+
outlives_requirement.free_region,
361+
outlives_requirement.outlived_free_region,
362+
outlives_requirement.blame_span,
363+
);
364+
}
302365

303-
Some(ClosureRegionRequirements {
304-
num_external_vids,
305-
outlives_requirements,
306-
})
366+
vec![]
307367
}
308368

309369
/// Check the final value for the free region `fr` to see if it
@@ -396,54 +456,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
396456
);
397457
}
398458

399-
/// Propagate the region constraints: this will grow the values
400-
/// for each region variable until all the constraints are
401-
/// satisfied. Note that some values may grow **too** large to be
402-
/// feasible, but we check this later.
403-
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
404-
let mut changed = true;
405-
406-
debug!("propagate_constraints()");
407-
debug!("propagate_constraints: constraints={:#?}", {
408-
let mut constraints: Vec<_> = self.constraints.iter().collect();
409-
constraints.sort();
410-
constraints
411-
});
412-
413-
// The initial values for each region are derived from the liveness
414-
// constraints we have accumulated.
415-
let mut inferred_values = self.liveness_constraints.clone();
416-
417-
while changed {
418-
changed = false;
419-
debug!("propagate_constraints: --------------------");
420-
for constraint in &self.constraints {
421-
debug!("propagate_constraints: constraint={:?}", constraint);
422-
423-
// Grow the value as needed to accommodate the
424-
// outlives constraint.
425-
let Ok(made_changes) = self.dfs(
426-
mir,
427-
CopyFromSourceToTarget {
428-
source_region: constraint.sub,
429-
target_region: constraint.sup,
430-
inferred_values: &mut inferred_values,
431-
constraint_point: constraint.point,
432-
},
433-
);
434-
435-
if made_changes {
436-
debug!("propagate_constraints: sub={:?}", constraint.sub);
437-
debug!("propagate_constraints: sup={:?}", constraint.sup);
438-
changed = true;
439-
}
440-
}
441-
debug!("\n");
442-
}
443-
444-
self.inferred_values = Some(inferred_values);
445-
}
446-
447459
/// Tries to finds a good span to blame for the fact that `fr1`
448460
/// contains `fr2`.
449461
fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
@@ -589,4 +601,3 @@ impl ClosureRegionRequirementsExt for ClosureRegionRequirements {
589601
}
590602
}
591603
}
592-

src/test/ui/nll/closure-requirements/escape-argument-callee.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error: free region `'_#4r` does not outlive free region `'_#3r`
1010
36 | let mut closure = expect_sig(|p, y| *p = y);
1111
| ^^^^^^
1212

13-
note: External requirements
13+
note: No external requirements
1414
--> $DIR/escape-argument-callee.rs:36:38
1515
|
1616
36 | let mut closure = expect_sig(|p, y| *p = y);
@@ -20,7 +20,6 @@ note: External requirements
2020
i16,
2121
for<'r, 's, 't0> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) i32))
2222
]
23-
= note: number of external vids: 1
2423

2524
note: No external requirements
2625
--> $DIR/escape-argument-callee.rs:30:1

src/test/ui/nll/closure-requirements/escape-argument.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
note: External requirements
1+
note: No external requirements
22
--> $DIR/escape-argument.rs:36:38
33
|
44
36 | let mut closure = expect_sig(|p, y| *p = y);
@@ -8,7 +8,6 @@ note: External requirements
88
i16,
99
for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) mut &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32))
1010
]
11-
= note: number of external vids: 1
1211

1312
note: No external requirements
1413
--> $DIR/escape-argument.rs:30:1

src/test/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error: free region `'_#5r` does not outlive free region `'_#6r`
1010
57 | demand_y(x, y, p)
1111
| ^
1212

13-
note: External requirements
13+
note: No external requirements
1414
--> $DIR/propagate-approximated-fail-no-postdom.rs:53:9
1515
|
1616
53 | / |_outlives1, _outlives2, _outlives3, x, y| {
@@ -26,7 +26,6 @@ note: External requirements
2626
i16,
2727
for<'r, 's> extern "rust-call" fn((std::cell::Cell<&'_#1r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&'_#2r &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#3r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
2828
]
29-
= note: number of external vids: 4
3029

3130
note: No external requirements
3231
--> $DIR/propagate-approximated-fail-no-postdom.rs:48:1

src/test/ui/nll/closure-requirements/propagate-approximated-shorter-to-static-comparing-against-free.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error: free region `'_#2r` does not outlive free region `'_#1r`
1010
33 | cell_a.set(cell_x.get()); // forces 'x: 'a, error in closure
1111
| ^^^^^^
1212

13-
note: External requirements
13+
note: No external requirements
1414
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:31:15
1515
|
1616
31 | foo(cell, |cell_a, cell_x| {
@@ -25,7 +25,6 @@ note: External requirements
2525
i32,
2626
for<'r> extern "rust-call" fn((std::cell::Cell<&'_#1r u32>, std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) u32>))
2727
]
28-
= note: number of external vids: 2
2928

3029
note: No external requirements
3130
--> $DIR/propagate-approximated-shorter-to-static-comparing-against-free.rs:28:1
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
warning: not reporting region error due to -Znll
2+
--> $DIR/propagate-approximated-to-empty.rs:41:9
3+
|
4+
41 | demand_y(x, y, x.get())
5+
| ^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: free region `'_#6r` does not outlive free region `'_#4r`
8+
--> $DIR/propagate-approximated-to-empty.rs:41:21
9+
|
10+
41 | demand_y(x, y, x.get())
11+
| ^
12+
13+
note: No external requirements
14+
--> $DIR/propagate-approximated-to-empty.rs:39:47
15+
|
16+
39 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
17+
| _______________________________________________^
18+
40 | | // Only works if 'x: 'y:
19+
41 | | demand_y(x, y, x.get())
20+
42 | | //~^ WARN not reporting region error due to -Znll
21+
43 | | //~| ERROR free region `'_#6r` does not outlive free region `'_#4r`
22+
44 | | });
23+
| |_____^
24+
|
25+
= note: defining type: DefId(0/1:18 ~ propagate_approximated_to_empty[317d]::supply[0]::{{closure}}[0]) with closure substs [
26+
i16,
27+
for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
28+
]
29+
30+
note: No external requirements
31+
--> $DIR/propagate-approximated-to-empty.rs:38:1
32+
|
33+
38 | / fn supply<'a, 'b>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>) {
34+
39 | | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
35+
40 | | // Only works if 'x: 'y:
36+
41 | | demand_y(x, y, x.get())
37+
... |
38+
44 | | });
39+
45 | | }
40+
| |_^
41+
|
42+
= note: defining type: DefId(0/0:6 ~ propagate_approximated_to_empty[317d]::supply[0]) with substs []
43+
44+
error: aborting due to previous error
45+

src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-no-bounds.stderr

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll
55
| ^^^^^^^^^^^^^^^^^^^^^^^
66

77
error: free region `'_#6r` does not outlive free region `'_#4r`
8-
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:21
8+
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:47:18
99
|
1010
47 | demand_y(x, y, x.get())
11-
| ^
11+
| ^
1212

13-
note: External requirements
13+
note: No external requirements
1414
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:45:47
1515
|
1616
45 | establish_relationships(&cell_a, &cell_b, |_outlives, x, y| {
@@ -26,7 +26,6 @@ note: External requirements
2626
i16,
2727
for<'r, 's, 't0, 't1, 't2> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>))
2828
]
29-
= note: number of external vids: 2
3029

3130
note: No external requirements
3231
--> $DIR/propagate-fail-to-approximate-longer-no-bounds.rs:44:1

src/test/ui/nll/closure-requirements/propagate-fail-to-approximate-longer-wrong-bounds.stderr

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ warning: not reporting region error due to -Znll
55
| ^^^^^^^^^^^^^^^^^^^^^^^
66

77
error: free region `'_#5r` does not outlive free region `'_#7r`
8-
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:21
8+
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:51:18
99
|
1010
51 | demand_y(x, y, x.get())
11-
| ^
11+
| ^
1212

13-
note: External requirements
13+
note: No external requirements
1414
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:49:47
1515
|
1616
49 | establish_relationships(&cell_a, &cell_b, |_outlives1, _outlives2, x, y| {
@@ -26,7 +26,6 @@ note: External requirements
2626
i16,
2727
for<'r, 's, 't0, 't1, 't2, 't3> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) &'_#1r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't0)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) &'_#2r u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't2)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) u32>, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't3)) std::cell::Cell<&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 't1)) u32>))
2828
]
29-
= note: number of external vids: 3
3029

3130
note: No external requirements
3231
--> $DIR/propagate-fail-to-approximate-longer-wrong-bounds.rs:48:1

src/test/ui/nll/closure-requirements/return-wrong-bound-region.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error: free region `'_#3r` does not outlive free region `'_#2r`
1010
21 | expect_sig(|a, b| b); // ought to return `a`
1111
| ^
1212

13-
note: External requirements
13+
note: No external requirements
1414
--> $DIR/return-wrong-bound-region.rs:21:16
1515
|
1616
21 | expect_sig(|a, b| b); // ought to return `a`
@@ -20,7 +20,6 @@ note: External requirements
2020
i16,
2121
for<'r, 's> extern "rust-call" fn((&ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32, &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 's)) i32)) -> &ReLateBound(DebruijnIndex { depth: 1 }, BrNamed(crate0:DefIndex(0:0), 'r)) i32
2222
]
23-
= note: number of external vids: 1
2423

2524
note: No external requirements
2625
--> $DIR/return-wrong-bound-region.rs:20:1

0 commit comments

Comments
 (0)