Skip to content

Commit ac92224

Browse files
committed
best_blame_constraint: don't filter constraints by sup SCC
The SCCs of the region graph are not a reliable heuristic to use for blaming an interesting constraint for diagnostics. For region errors, if the outlived region is `'static`, or the involved types are invariant in their lifetiems, there will be cycles in the constraint graph containing both the target region and the most interesting constraints to blame. To get better diagnostics in these cases, this commit removes that heuristic.
1 parent 2249232 commit ac92224

File tree

102 files changed

+592
-514
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+592
-514
lines changed

compiler/rustc_borrowck/src/region_infer/mod.rs

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,7 +1945,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19451945
target_test: impl Fn(RegionVid) -> bool,
19461946
) -> (BlameConstraint<'tcx>, Vec<OutlivesConstraint<'tcx>>) {
19471947
// Find all paths
1948-
let (path, target_region) = self
1948+
let (path, _) = self
19491949
.find_constraint_paths_between_regions(from_region, target_test)
19501950
.or_else(|| {
19511951
self.find_constraint_paths_between_regions(from_region, |r| {
@@ -1983,29 +1983,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
19831983
})
19841984
.unwrap_or_else(|| ObligationCauseCode::Misc);
19851985

1986-
// To find the best span to cite, we first try to look for the
1987-
// final constraint that is interesting and where the `sup` is
1988-
// not unified with the ultimate target region. The reason
1989-
// for this is that we have a chain of constraints that lead
1990-
// from the source to the target region, something like:
1991-
//
1992-
// '0: '1 ('0 is the source)
1993-
// '1: '2
1994-
// '2: '3
1995-
// '3: '4
1996-
// '4: '5
1997-
// '5: '6 ('6 is the target)
1998-
//
1999-
// Some of those regions are unified with `'6` (in the same
2000-
// SCC). We want to screen those out. After that point, the
2001-
// "closest" constraint we have to the end is going to be the
2002-
// most likely to be the point where the value escapes -- but
2003-
// we still want to screen for an "interesting" point to
2004-
// highlight (e.g., a call site or something).
2005-
let target_scc = self.constraint_sccs.scc(target_region);
2006-
2007-
// As noted above, when reporting an error, there is typically a chain of constraints
2008-
// leading from some "source" region which must outlive some "target" region.
1986+
// When reporting an error, there is typically a chain of constraints leading from some
1987+
// "source" region which must outlive some "target" region.
20091988
// In most cases, we prefer to "blame" the constraints closer to the target --
20101989
// but there is one exception. When constraints arise from higher-ranked subtyping,
20111990
// we generally prefer to blame the source value,
@@ -2047,30 +2026,14 @@ impl<'tcx> RegionInferenceContext<'tcx> {
20472026
};
20482027

20492028
let interesting_to_blame = |constraint: &OutlivesConstraint<'tcx>| {
2050-
let constraint_sup_scc = self.constraint_sccs.scc(constraint.sup);
2051-
2052-
if blame_source {
2053-
match constraint.category {
2054-
ConstraintCategory::OpaqueType
2029+
!matches!(
2030+
constraint.category,
2031+
ConstraintCategory::OpaqueType
20552032
| ConstraintCategory::Boring
20562033
| ConstraintCategory::BoringNoLocation
20572034
| ConstraintCategory::Internal
2058-
| ConstraintCategory::Predicate(_) => false,
2059-
ConstraintCategory::TypeAnnotation
2060-
| ConstraintCategory::Return(_)
2061-
| ConstraintCategory::Yield => true,
2062-
_ => constraint_sup_scc != target_scc,
2063-
}
2064-
} else {
2065-
!matches!(
2066-
constraint.category,
2067-
ConstraintCategory::OpaqueType
2068-
| ConstraintCategory::Boring
2069-
| ConstraintCategory::BoringNoLocation
2070-
| ConstraintCategory::Internal
2071-
| ConstraintCategory::Predicate(_)
2072-
)
2073-
}
2035+
| ConstraintCategory::Predicate(_)
2036+
)
20742037
};
20752038

20762039
let best_choice = if blame_source {
@@ -2111,10 +2074,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
21112074
Some(i) => path[i],
21122075

21132076
None => {
2114-
// If that search fails, that is.. unusual. Maybe everything
2115-
// is in the same SCC or something. In that case, find what
2116-
// appears to be the most interesting point to report to the
2117-
// user via an even more ad-hoc guess.
2077+
// If that search fails, the only constraints on the path are those that we try not
2078+
// to blame. In that case, find what appears to be the most interesting point to
2079+
// report to the user via an even more ad-hoc guess.
21182080
*path.iter().min_by_key(|p| p.category).unwrap()
21192081
}
21202082
};

tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>(
77
| lifetime `'a` defined here
88
...
99
LL | let z: I::A = if cond { x } else { y };
10-
| ^ assignment requires that `'a` must outlive `'b`
10+
| ^ assignment requires that `'b` must outlive `'a`
1111
|
12-
= help: consider adding the following bound: `'a: 'b`
12+
= help: consider adding the following bound: `'b: 'a`
1313

1414
error: lifetime may not live long enough
1515
--> $DIR/associated-types-project-from-hrtb-in-fn-body.rs:22:40
@@ -20,9 +20,9 @@ LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>(
2020
| lifetime `'a` defined here
2121
...
2222
LL | let z: I::A = if cond { x } else { y };
23-
| ^ assignment requires that `'b` must outlive `'a`
23+
| ^ assignment requires that `'a` must outlive `'b`
2424
|
25-
= help: consider adding the following bound: `'b: 'a`
25+
= help: consider adding the following bound: `'a: 'b`
2626

2727
help: `'a` and `'b` must be the same: replace one with the other
2828

tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
77
| lifetime `'a` defined here
88
LL | let f = foo; // <-- No consistent type can be inferred for `f` here.
99
LL | let a = bar(f, x);
10-
| ^^^^^^^^^ argument requires that `'a` must outlive `'b`
10+
| ^^^^^^^^^ argument requires that `'b` must outlive `'a`
1111
|
12-
= help: consider adding the following bound: `'a: 'b`
12+
= help: consider adding the following bound: `'b: 'a`
1313
= note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant
1414
= note: the struct `Type<'a>` is invariant over the parameter `'a`
1515
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
@@ -23,9 +23,9 @@ LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) {
2323
| lifetime `'a` defined here
2424
...
2525
LL | let b = bar(f, y);
26-
| ^^^^^^^^^ argument requires that `'b` must outlive `'a`
26+
| ^^^^^^^^^ argument requires that `'a` must outlive `'b`
2727
|
28-
= help: consider adding the following bound: `'b: 'a`
28+
= help: consider adding the following bound: `'a: 'b`
2929
= note: requirement occurs because of the type `Type<'_>`, which makes the generic argument `'_` invariant
3030
= note: the struct `Type<'a>` is invariant over the parameter `'a`
3131
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

tests/ui/async-await/async-closures/without-precise-captures-we-are-powerless.stderr

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | let c = async || { println!("{}", *x); };
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
88
LL | outlives::<'a>(c());
99
LL | outlives::<'a>(call_once(c));
10-
| ------------ argument requires that `x` is borrowed for `'a`
10+
| ---------------------------- argument requires that `x` is borrowed for `'a`
1111
...
1212
LL | }
1313
| - `x` dropped here while still borrowed
@@ -21,10 +21,10 @@ LL | fn simple<'a>(x: &'a i32) {
2121
LL | let c = async move || { println!("{}", *x); };
2222
| - binding `c` declared here
2323
LL | outlives::<'a>(c());
24-
| ^--
25-
| |
26-
| borrowed value does not live long enough
27-
| argument requires that `c` is borrowed for `'a`
24+
| ---------------^---
25+
| | |
26+
| | borrowed value does not live long enough
27+
| argument requires that `c` is borrowed for `'a`
2828
LL | outlives::<'a>(call_once(c));
2929
LL | }
3030
| - `c` dropped here while still borrowed
@@ -38,7 +38,7 @@ LL | let c = async || { println!("{}", *x.0); };
3838
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
3939
LL | outlives::<'a>(c());
4040
LL | outlives::<'a>(call_once(c));
41-
| ------------ argument requires that `x` is borrowed for `'a`
41+
| ---------------------------- argument requires that `x` is borrowed for `'a`
4242
...
4343
LL | }
4444
| - `x` dropped here while still borrowed
@@ -52,7 +52,7 @@ LL | let c = async || { println!("{}", *x.0); };
5252
| ---------------------------------- borrow of `x` occurs here
5353
LL | outlives::<'a>(c());
5454
LL | outlives::<'a>(call_once(c));
55-
| ------------ argument requires that `x` is borrowed for `'a`
55+
| ---------------------------- argument requires that `x` is borrowed for `'a`
5656
LL |
5757
LL | let c = async move || { println!("{}", *x.0); };
5858
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ move out of `x` occurs here
@@ -66,10 +66,10 @@ LL | fn through_field<'a>(x: S<'a>) {
6666
LL | let c = async move || { println!("{}", *x.0); };
6767
| - binding `c` declared here
6868
LL | outlives::<'a>(c());
69-
| ^--
70-
| |
71-
| borrowed value does not live long enough
72-
| argument requires that `c` is borrowed for `'a`
69+
| ---------------^---
70+
| | |
71+
| | borrowed value does not live long enough
72+
| argument requires that `c` is borrowed for `'a`
7373
LL | outlives::<'a>(call_once(c));
7474
LL | }
7575
| - `c` dropped here while still borrowed
@@ -83,10 +83,10 @@ LL | fn through_field<'a>(x: S<'a>) {
8383
LL | let c = async move || { println!("{}", *x.0); };
8484
| - binding `c` declared here
8585
LL | outlives::<'a>(c());
86-
| ---
87-
| |
88-
| borrow of `c` occurs here
89-
| argument requires that `c` is borrowed for `'a`
86+
| -------------------
87+
| | |
88+
| | borrow of `c` occurs here
89+
| argument requires that `c` is borrowed for `'a`
9090
LL | outlives::<'a>(call_once(c));
9191
| ^ move out of `c` occurs here
9292

@@ -99,18 +99,18 @@ LL | let c = async || { println!("{}", *x.0); };
9999
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
100100
LL | outlives::<'a>(c());
101101
LL | outlives::<'a>(call_once(c));
102-
| ------------ argument requires that `x` is borrowed for `'a`
102+
| ---------------------------- argument requires that `x` is borrowed for `'a`
103103
LL | }
104104
| - `x` dropped here while still borrowed
105105

106106
error[E0621]: explicit lifetime required in the type of `x`
107-
--> $DIR/without-precise-captures-we-are-powerless.rs:38:20
107+
--> $DIR/without-precise-captures-we-are-powerless.rs:38:5
108108
|
109109
LL | fn through_field_and_ref<'a>(x: &S<'a>) {
110110
| ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>`
111111
...
112112
LL | outlives::<'a>(call_once(c));
113-
| ^^^^^^^^^^^^ lifetime `'a` required
113+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required
114114

115115
error[E0597]: `c` does not live long enough
116116
--> $DIR/without-precise-captures-we-are-powerless.rs:43:20
@@ -120,22 +120,22 @@ LL | fn through_field_and_ref_move<'a>(x: &S<'a>) {
120120
LL | let c = async move || { println!("{}", *x.0); };
121121
| - binding `c` declared here
122122
LL | outlives::<'a>(c());
123-
| ^--
124-
| |
125-
| borrowed value does not live long enough
126-
| argument requires that `c` is borrowed for `'a`
123+
| ---------------^---
124+
| | |
125+
| | borrowed value does not live long enough
126+
| argument requires that `c` is borrowed for `'a`
127127
LL | outlives::<'a>(call_once(c));
128128
LL | }
129129
| - `c` dropped here while still borrowed
130130

131131
error[E0621]: explicit lifetime required in the type of `x`
132-
--> $DIR/without-precise-captures-we-are-powerless.rs:44:20
132+
--> $DIR/without-precise-captures-we-are-powerless.rs:44:5
133133
|
134134
LL | fn through_field_and_ref_move<'a>(x: &S<'a>) {
135135
| ------ help: add explicit lifetime `'a` to the type of `x`: `&'a S<'a>`
136136
...
137137
LL | outlives::<'a>(call_once(c));
138-
| ^^^^^^^^^^^^ lifetime `'a` required
138+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required
139139

140140
error: aborting due to 10 previous errors
141141

tests/ui/async-await/issue-76547.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ impl<'a> Future for ListFut<'a> {
1717
}
1818

1919
async fn fut(bufs: &mut [&mut [u8]]) {
20-
ListFut(bufs).await
2120
//~^ ERROR lifetime may not live long enough
21+
ListFut(bufs).await
2222
}
2323

2424
pub struct ListFut2<'a>(&'a mut [&'a mut [u8]]);
@@ -31,8 +31,8 @@ impl<'a> Future for ListFut2<'a> {
3131
}
3232

3333
async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
34-
ListFut2(bufs).await
3534
//~^ ERROR lifetime may not live long enough
35+
ListFut2(bufs).await
3636
}
3737

3838
fn main() {}

tests/ui/async-await/issue-76547.stderr

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
error: lifetime may not live long enough
2-
--> $DIR/issue-76547.rs:20:13
2+
--> $DIR/issue-76547.rs:19:14
33
|
44
LL | async fn fut(bufs: &mut [&mut [u8]]) {
5-
| - - let's call the lifetime of this reference `'2`
6-
| |
7-
| let's call the lifetime of this reference `'1`
8-
LL | ListFut(bufs).await
9-
| ^^^^ this usage requires that `'1` must outlive `'2`
5+
| ^^^^ - - let's call the lifetime of this reference `'2`
6+
| | |
7+
| | let's call the lifetime of this reference `'1`
8+
| assignment requires that `'1` must outlive `'2`
109
|
1110
help: consider introducing a named lifetime parameter
1211
|
1312
LL | async fn fut<'a>(bufs: &'a mut [&'a mut [u8]]) {
1413
| ++++ ++ ++
1514

1615
error: lifetime may not live long enough
17-
--> $DIR/issue-76547.rs:34:14
16+
--> $DIR/issue-76547.rs:33:15
1817
|
1918
LL | async fn fut2(bufs: &mut [&mut [u8]]) -> i32 {
20-
| - - let's call the lifetime of this reference `'2`
21-
| |
22-
| let's call the lifetime of this reference `'1`
23-
LL | ListFut2(bufs).await
24-
| ^^^^ this usage requires that `'1` must outlive `'2`
19+
| ^^^^ - - let's call the lifetime of this reference `'2`
20+
| | |
21+
| | let's call the lifetime of this reference `'1`
22+
| assignment requires that `'1` must outlive `'2`
2523
|
2624
help: consider introducing a named lifetime parameter
2725
|

tests/ui/borrowck/fn-item-check-type-params.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ LL | extend_lt(val);
1212
| argument requires that `'a` must outlive `'static`
1313

1414
error: lifetime may not live long enough
15-
--> $DIR/fn-item-check-type-params.rs:39:12
15+
--> $DIR/fn-item-check-type-params.rs:39:31
1616
|
1717
LL | pub fn test_coercion<'a>() {
1818
| -- lifetime `'a` defined here
1919
LL | let _: fn(&'a str) -> _ = extend_lt;
20-
| ^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`
20+
| ^^^^^^^^^ coercion requires that `'a` must outlive `'static`
2121

2222
error[E0716]: temporary value dropped while borrowed
2323
--> $DIR/fn-item-check-type-params.rs:48:11

tests/ui/borrowck/implementation-not-general-enough-ice-133252.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ LL | async {
1515
LL | let not_static = 0;
1616
| ---------- binding `not_static` declared here
1717
LL | force_send(async_load(&not_static));
18-
| -----------^^^^^^^^^^^-
19-
| | |
20-
| | borrowed value does not live long enough
21-
| argument requires that `not_static` is borrowed for `'1`
18+
| ----------------------^^^^^^^^^^^--
19+
| | |
20+
| | borrowed value does not live long enough
21+
| argument requires that `not_static` is borrowed for `'1`
2222
...
2323
LL | }
2424
| - `not_static` dropped here while still borrowed

tests/ui/borrowck/issue-7573.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub fn remove_package_from_database() {
1717
lines_to_use.push(installed_id);
1818
//~^ ERROR borrowed data escapes outside of closure
1919
//~| NOTE `installed_id` escapes the closure body here
20+
//~| NOTE requirement occurs because of a mutable reference to `Vec<&CrateId>`
21+
//~| NOTE mutable references are invariant over their type parameter
2022
};
2123
list_database(push_id);
2224

tests/ui/borrowck/issue-7573.stderr

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ LL | let push_id = |installed_id: &CrateId| {
99
LL |
1010
LL | lines_to_use.push(installed_id);
1111
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `installed_id` escapes the closure body here
12+
|
13+
= note: requirement occurs because of a mutable reference to `Vec<&CrateId>`
14+
= note: mutable references are invariant over their type parameter
15+
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
1216

1317
error: aborting due to 1 previous error
1418

0 commit comments

Comments
 (0)