Skip to content

Commit 0627df9

Browse files
Propagate region error for every non-local lower bound
1 parent 29ad85c commit 0627df9

File tree

6 files changed

+155
-92
lines changed

6 files changed

+155
-92
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,49 +1275,81 @@ impl<'tcx> RegionInferenceContext<'tcx> {
12751275
shorter_fr: RegionVid,
12761276
propagated_outlives_requirements: &mut Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
12771277
) -> RegionRelationCheckResult {
1278-
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements
1279-
// Shrink `longer_fr` until we find a non-local region (if we do).
1280-
// We'll call it `fr-` -- it's ever so slightly smaller than
1278+
if let Some(propagated_outlives_requirements) = propagated_outlives_requirements {
1279+
// Shrink `longer_fr` until we find some non-local regions.
1280+
// We'll call them `longer_fr-` -- they are ever so slightly smaller than
12811281
// `longer_fr`.
1282-
&& let Some(fr_minus) = self.universal_region_relations.non_local_lower_bound(longer_fr)
1283-
{
1284-
debug!("try_propagate_universal_region_error: fr_minus={:?}", fr_minus);
1282+
let longer_fr_minus = self.universal_region_relations.non_local_lower_bounds(longer_fr);
1283+
1284+
debug!("try_propagate_universal_region_error: fr_minus={:?}", longer_fr_minus);
1285+
1286+
// If we don't find a any non-local regions, we should error out as there is nothing
1287+
// to propagate.
1288+
if longer_fr_minus.is_empty() {
1289+
return RegionRelationCheckResult::Error;
1290+
}
12851291

12861292
let blame_constraint = self
12871293
.best_blame_constraint(longer_fr, NllRegionVariableOrigin::FreeRegion, shorter_fr)
12881294
.0;
12891295

1290-
// Grow `shorter_fr` until we find some non-local regions. (We
1291-
// always will.) We'll call them `shorter_fr+` -- they're ever
1292-
// so slightly larger than `shorter_fr`.
1296+
// Grow `shorter_fr` until we find some non-local regions.
1297+
// We will always find at least one: `'static`. We'll call
1298+
// them `shorter_fr+` -- they're ever so slightly larger
1299+
// than `shorter_fr`.
12931300
let shorter_fr_plus =
12941301
self.universal_region_relations.non_local_upper_bounds(shorter_fr);
1302+
debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus);
1303+
1304+
// We then create constraints `longer_fr-: shorter_fr+` that may or may not
1305+
// be propagated (see below).
1306+
let mut constraints = vec![];
1307+
for fr_minus in longer_fr_minus {
1308+
for shorter_fr_plus in &shorter_fr_plus {
1309+
constraints.push((fr_minus, *shorter_fr_plus));
1310+
}
1311+
}
12951312

1296-
// If any of the `shorter_fr+` regions are already outlived by `fr-`, we propagate only those.
1297-
// Otherwise, we might incorrectly reject valid code.
1313+
// We only need to propagate at least one of the constraints for
1314+
// soundness. However, we want to avoid arbitrary choices here
1315+
// and currently don't support returning OR constraints.
1316+
//
1317+
// If any of the `shorter_fr+` regions are already outlived by `longer_fr-`,
1318+
// we propagate only those.
12981319
//
12991320
// Consider this example (`'b: 'a` == `a -> b`), where we try to propagate `'d: 'a`:
13001321
// a --> b --> d
13011322
// \
13021323
// \-> c
13031324
// Here, `shorter_fr+` of `'a` == `['b, 'c]`.
1304-
// Propagating `'d: 'b` is correct and should occur; `'d: 'c` is redundant because of `'d: 'b`
1305-
// and could reject valid code.
1325+
// Propagating `'d: 'b` is correct and should occur; `'d: 'c` is redundant because of
1326+
// `'d: 'b` and could reject valid code.
13061327
//
1307-
// So we filter `shorter_fr+` to regions already outlived by `fr-`, but if the filter yields an empty set,
1308-
// we fall back to the original one.
1309-
let subset: Vec<_> = shorter_fr_plus
1328+
// So we filter the constraints to regions already outlived by `longer_fr-`, but if
1329+
// the filter yields an empty set, we fall back to the original one.
1330+
let subset: Vec<_> = constraints
13101331
.iter()
1311-
.filter(|&&fr_plus| self.eval_outlives(fr_minus, fr_plus))
1332+
.filter(|&&(fr_minus, shorter_fr_plus)| {
1333+
self.eval_outlives(fr_minus, shorter_fr_plus)
1334+
})
13121335
.copied()
13131336
.collect();
1314-
let shorter_fr_plus = if subset.is_empty() { shorter_fr_plus } else { subset };
1315-
debug!("try_propagate_universal_region_error: shorter_fr_plus={:?}", shorter_fr_plus);
1316-
for fr in shorter_fr_plus {
1317-
// Push the constraint `fr-: shorter_fr+`
1337+
let propagated_constraints = if subset.is_empty() { constraints } else { subset };
1338+
debug!(
1339+
"try_propagate_universal_region_error: constraints={:?}",
1340+
propagated_constraints
1341+
);
1342+
1343+
assert!(
1344+
!propagated_constraints.is_empty(),
1345+
"Expected at least one constraint to propagate here"
1346+
);
1347+
1348+
for (fr_minus, fr_plus) in propagated_constraints {
1349+
// Push the constraint `long_fr-: shorter_fr+`
13181350
propagated_outlives_requirements.push(ClosureOutlivesRequirement {
13191351
subject: ClosureOutlivesSubject::Region(fr_minus),
1320-
outlived_free_region: fr,
1352+
outlived_free_region: fr_plus,
13211353
blame_span: blame_constraint.cause.span,
13221354
category: blame_constraint.category,
13231355
});

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -94,28 +94,10 @@ impl UniversalRegionRelations<'_> {
9494
/// words, returns the largest (*) known region `fr1` that (a) is
9595
/// outlived by `fr` and (b) is not local.
9696
///
97-
/// (*) If there are multiple competing choices, we pick the "postdominating"
98-
/// one. See `TransitiveRelation::postdom_upper_bound` for details.
99-
pub(crate) fn non_local_lower_bound(&self, fr: RegionVid) -> Option<RegionVid> {
97+
/// (*) If there are multiple competing choices, we return all of them.
98+
pub(crate) fn non_local_lower_bounds(&self, fr: RegionVid) -> Vec<RegionVid> {
10099
debug!("non_local_lower_bound(fr={:?})", fr);
101-
let lower_bounds = self.non_local_bounds(&self.outlives, fr);
102-
103-
// In case we find more than one, reduce to one for
104-
// convenience. This is to prevent us from generating more
105-
// complex constraints, but it will cause spurious errors.
106-
let post_dom = self.outlives.mutual_immediate_postdominator(lower_bounds);
107-
108-
debug!("non_local_bound: post_dom={:?}", post_dom);
109-
110-
post_dom.and_then(|post_dom| {
111-
// If the mutual immediate postdom is not local, then
112-
// there is no non-local result we can return.
113-
if !self.universal_regions.is_local_free_region(post_dom) {
114-
Some(post_dom)
115-
} else {
116-
None
117-
}
118-
})
100+
self.non_local_bounds(&self.outlives, fr)
119101
}
120102

121103
/// Helper for `non_local_upper_bounds` and `non_local_lower_bounds`.

tests/ui/nll/closure-requirements/propagate-approximated-fail-no-postdom.rs renamed to tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// Test where we fail to approximate due to demanding a postdom
2-
// relationship between our upper bounds.
3-
1+
// Test that we can propagate multiple region errors for closure constraints
2+
// where the longer region has multiple non-local lower bounds without any postdominating one.
43
//@ compile-flags:-Zverbose-internals
54

65
#![feature(rustc_attrs)]
@@ -13,9 +12,8 @@ use std::cell::Cell;
1312
// 'x: 'b
1413
// 'c: 'y
1514
//
16-
// we have to prove that `'x: 'y`. We currently can only approximate
17-
// via a postdominator -- hence we fail to choose between `'a` and
18-
// `'b` here and report the error in the closure.
15+
// we have to prove that `'x: 'y`. We find non-local lower bounds of 'x to be 'a and 'b and
16+
// non-local upper bound of 'y to be 'c. So we propagate `'b: 'c` and `'a: 'c`.
1917
fn establish_relationships<'a, 'b, 'c, F>(
2018
_cell_a: Cell<&'a u32>,
2119
_cell_b: Cell<&'b u32>,
@@ -36,14 +34,16 @@ fn demand_y<'x, 'y>(_cell_x: Cell<&'x u32>, _cell_y: Cell<&'y u32>, _y: &'y u32)
3634

3735
#[rustc_regions]
3836
fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
37+
//~vv ERROR lifetime may not live long enough
38+
//~v ERROR lifetime may not live long enough
3939
establish_relationships(
4040
cell_a,
4141
cell_b,
4242
cell_c,
4343
|_outlives1, _outlives2, _outlives3, x, y| {
4444
// Only works if 'x: 'y:
4545
let p = x.get();
46-
demand_y(x, y, p) //~ ERROR
46+
demand_y(x, y, p)
4747
},
4848
);
4949
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
note: external requirements
2+
--> $DIR/propagate-approximated-both-lower-bounds.rs:43:9
3+
|
4+
LL | |_outlives1, _outlives2, _outlives3, x, y| {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: defining type: supply::{closure#0} with closure args [
8+
i16,
9+
for<Region(BrAnon), Region(BrAnon)> extern "rust-call" fn((std::cell::Cell<&'?1 &'^0 u32>, std::cell::Cell<&'?2 &'^0 u32>, std::cell::Cell<&'^1 &'?3 u32>, std::cell::Cell<&'^0 u32>, std::cell::Cell<&'^1 u32>)),
10+
(),
11+
]
12+
= note: late-bound region is '?7
13+
= note: late-bound region is '?8
14+
= note: late-bound region is '?4
15+
= note: late-bound region is '?5
16+
= note: late-bound region is '?6
17+
= note: number of external vids: 7
18+
= note: where '?2: '?3
19+
= note: where '?1: '?3
20+
21+
note: no external requirements
22+
--> $DIR/propagate-approximated-both-lower-bounds.rs:36:1
23+
|
24+
LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26+
|
27+
= note: defining type: supply
28+
29+
error: lifetime may not live long enough
30+
--> $DIR/propagate-approximated-both-lower-bounds.rs:39:5
31+
|
32+
LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
33+
| -- -- lifetime `'c` defined here
34+
| |
35+
| lifetime `'a` defined here
36+
...
37+
LL | / establish_relationships(
38+
LL | | cell_a,
39+
LL | | cell_b,
40+
LL | | cell_c,
41+
... |
42+
LL | | },
43+
LL | | );
44+
| |_____^ argument requires that `'a` must outlive `'c`
45+
|
46+
= help: consider adding the following bound: `'a: 'c`
47+
= note: requirement occurs because of the type `Cell<&'?10 u32>`, which makes the generic argument `&'?10 u32` invariant
48+
= note: the struct `Cell<T>` is invariant over the parameter `T`
49+
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
50+
51+
error: lifetime may not live long enough
52+
--> $DIR/propagate-approximated-both-lower-bounds.rs:39:5
53+
|
54+
LL | fn supply<'a, 'b, 'c>(cell_a: Cell<&'a u32>, cell_b: Cell<&'b u32>, cell_c: Cell<&'c u32>) {
55+
| -- -- lifetime `'c` defined here
56+
| |
57+
| lifetime `'b` defined here
58+
...
59+
LL | / establish_relationships(
60+
LL | | cell_a,
61+
LL | | cell_b,
62+
LL | | cell_c,
63+
... |
64+
LL | | },
65+
LL | | );
66+
| |_____^ argument requires that `'b` must outlive `'c`
67+
|
68+
= help: consider adding the following bound: `'b: 'c`
69+
= note: requirement occurs because of the type `Cell<&'?10 u32>`, which makes the generic argument `&'?10 u32` invariant
70+
= note: the struct `Cell<T>` is invariant over the parameter `T`
71+
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
72+
73+
help: the following changes may resolve your lifetime errors
74+
|
75+
= help: add bound `'a: 'c`
76+
= help: add bound `'b: 'c`
77+
78+
error: aborting due to 2 previous errors
79+

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

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//@ check-pass
2+
// This test checks that the compiler propagates outlives requirements for both
3+
// non-local lower bounds ['a, 'b] of '_, instead of conservatively finding a post-dominiting one
4+
// from those 2.
5+
6+
struct MyTy<'a, 'b, 'x>(std::cell::Cell<(&'a &'x str, &'b &'x str)>);
7+
fn wf<T>(_: T) {}
8+
fn test<'a, 'b, 'x>() {
9+
|x: MyTy<'a, 'b, '_>| wf(x);
10+
}
11+
12+
fn main() {}

0 commit comments

Comments
 (0)