Skip to content

Commit f213957

Browse files
authoredJul 1, 2020
Rollup merge of #73806 - Aaron1011:feature/approx-universal-upper, r=estebank
Use an 'approximate' universal upper bound when reporting region errors Fixes #67765 When reporting errors during MIR region inference, we sometimes use `universal_upper_bound` to obtain a named universal region that we can display to the user. However, this is not always possible - in a case like `fn foo<'a, 'b>() { .. }`, the only upper bound for a region containing `'a` and `'b` is `'static`. When displaying diagnostics, it's usually better to display *some* named region (even if there are multiple involved) rather than fall back to a generic error involving `'static`. This commit adds a new `approx_universal_upper_bound` method, which uses the lowest-numbered universal region if the only alternative is to return `'static`.
2 parents 178b0c2 + 517d361 commit f213957

9 files changed

+101
-12
lines changed
 

‎src/librustc_mir/borrow_check/diagnostics/region_errors.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
122122
if self.regioncx.universal_regions().is_universal_region(r) {
123123
Some(r)
124124
} else {
125-
let upper_bound = self.regioncx.universal_upper_bound(r);
125+
// We just want something nameable, even if it's not
126+
// actually an upper bound.
127+
let upper_bound = self.regioncx.approx_universal_upper_bound(r);
126128

127129
if self.regioncx.upper_bound_in_region_scc(r, upper_bound) {
128130
self.to_error_region_vid(upper_bound)

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

+34
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,40 @@ impl<'tcx> RegionInferenceContext<'tcx> {
11141114
lub
11151115
}
11161116

1117+
/// Like `universal_upper_bound`, but returns an approximation more suitable
1118+
/// for diagnostics. If `r` contains multiple disjoint universal regions
1119+
/// (e.g. 'a and 'b in `fn foo<'a, 'b> { ... }`, we pick the lower-numbered region.
1120+
/// This corresponds to picking named regions over unnamed regions
1121+
/// (e.g. picking early-bound regions over a closure late-bound region).
1122+
///
1123+
/// This means that the returned value may not be a true upper bound, since
1124+
/// only 'static is known to outlive disjoint universal regions.
1125+
/// Therefore, this method should only be used in diagnostic code,
1126+
/// where displaying *some* named universal region is better than
1127+
/// falling back to 'static.
1128+
pub(in crate::borrow_check) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
1129+
debug!("approx_universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
1130+
1131+
// Find the smallest universal region that contains all other
1132+
// universal regions within `region`.
1133+
let mut lub = self.universal_regions.fr_fn_body;
1134+
let r_scc = self.constraint_sccs.scc(r);
1135+
let static_r = self.universal_regions.fr_static;
1136+
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
1137+
let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
1138+
debug!("approx_universal_upper_bound: ur={:?} lub={:?} new_lub={:?}", ur, lub, new_lub);
1139+
if ur != static_r && lub != static_r && new_lub == static_r {
1140+
lub = std::cmp::min(ur, lub);
1141+
} else {
1142+
lub = new_lub;
1143+
}
1144+
}
1145+
1146+
debug!("approx_universal_upper_bound: r={:?} lub={:?}", r, lub);
1147+
1148+
lub
1149+
}
1150+
11171151
/// Tests if `test` is true when applied to `lower_bound` at
11181152
/// `point`.
11191153
fn eval_verify_bound(

‎src/librustc_mir/borrow_check/region_infer/opaque_types.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
141141
{
142142
tcx.fold_regions(&ty, &mut false, |region, _| match *region {
143143
ty::ReVar(vid) => {
144-
let upper_bound = self.universal_upper_bound(vid);
144+
// Find something that we can name
145+
let upper_bound = self.approx_universal_upper_bound(vid);
145146
self.definitions[upper_bound].external_name.unwrap_or(region)
146147
}
147148
_ => region,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// edition:2018
2+
//
3+
// Regression test for issue #67765
4+
// Tests that we point at the proper location when giving
5+
// a lifetime error.
6+
fn main() {}
7+
8+
async fn func<'a>() -> Result<(), &'a str> {
9+
let s = String::new();
10+
11+
let b = &s[..];
12+
13+
Err(b)?; //~ ERROR cannot return value referencing local variable `s`
14+
15+
Ok(())
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0515]: cannot return value referencing local variable `s`
2+
--> $DIR/issue-67765-async-diagnostic.rs:13:11
3+
|
4+
LL | let b = &s[..];
5+
| - `s` is borrowed here
6+
LL |
7+
LL | Err(b)?;
8+
| ^ returns a value referencing data owned by the current function
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0515`.
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
fn main() {
2-
[0].iter().flat_map(|a| [0].iter().map(|_| &a)); //~ ERROR `a` does not live long enough
2+
[0].iter().flat_map(|a| [0].iter().map(|_| &a)); //~ ERROR closure may outlive
33
}
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
error[E0597]: `a` does not live long enough
2-
--> $DIR/unnamed-closure-doesnt-life-long-enough-issue-67634.rs:2:49
1+
error[E0373]: closure may outlive the current function, but it borrows `a`, which is owned by the current function
2+
--> $DIR/unnamed-closure-doesnt-life-long-enough-issue-67634.rs:2:44
33
|
44
LL | [0].iter().flat_map(|a| [0].iter().map(|_| &a));
5-
| - ^- ...but `a` will be dropped here, when the enclosing closure returns
6-
| | |
7-
| | `a` would have to be valid for `'_`...
8-
| has type `&i32`
5+
| ^^^ - `a` is borrowed here
6+
| |
7+
| may outlive borrowed value `a`
98
|
10-
= note: functions cannot return a borrow to data owned within the function's scope, functions can only return borrows to data passed as arguments
11-
= note: to learn more, visit <https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#dangling-references>
9+
note: closure is returned here
10+
--> $DIR/unnamed-closure-doesnt-life-long-enough-issue-67634.rs:2:29
11+
|
12+
LL | [0].iter().flat_map(|a| [0].iter().map(|_| &a));
13+
| ^^^^^^^^^^^^^^^^^^^^^^
14+
help: to force the closure to take ownership of `a` (and any other referenced variables), use the `move` keyword
15+
|
16+
LL | [0].iter().flat_map(|a| [0].iter().map(move |_| &a));
17+
| ^^^^^^^^
1218

1319
error: aborting due to previous error
1420

15-
For more information about this error, try `rustc --explain E0597`.
21+
For more information about this error, try `rustc --explain E0373`.
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// See https://github.com/rust-lang/rust/pull/67911#issuecomment-576023915
2+
fn f<'a, 'b>(x: i32) -> (&'a i32, &'b i32) {
3+
let y = &x;
4+
(y, y) //~ ERROR cannot return
5+
}
6+
7+
fn main() {}
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0515]: cannot return value referencing function parameter `x`
2+
--> $DIR/return-disjoint-regions.rs:4:5
3+
|
4+
LL | let y = &x;
5+
| -- `x` is borrowed here
6+
LL | (y, y)
7+
| ^^^^^^ returns a value referencing data owned by the current function
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0515`.

0 commit comments

Comments
 (0)
Please sign in to comment.