Skip to content

Commit e3a048c

Browse files
committed
Ensure that early-bound function lifetimes are always 'local'
During borrowchecking, we treat any free (early-bound) regions on the 'defining type' as `RegionClassification::External`. According to the doc comments, we should only have 'external' regions when checking a closure/generator. However, a plain function can also have some if its regions be considered 'early bound' - this occurs when the region is constrained by an argument, appears in a `where` clause, or in an opaque type. This was causing us to incorrectly mark these regions as 'external', which caused some diagnostic code to act as if we were referring to a 'parent' region from inside a closure. This PR marks all instantiated region variables as 'local' when we're borrow-checking something other than a closure/generator/inline-const.
1 parent 984a6bf commit e3a048c

38 files changed

+98
-124
lines changed

compiler/rustc_borrowck/src/universal_regions.rs

+25-16
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,9 @@ pub enum RegionClassification {
180180
/// anywhere. There is only one, `'static`.
181181
Global,
182182

183-
/// An **external** region is only relevant for closures. In that
184-
/// case, it refers to regions that are free in the closure type
183+
/// An **external** region is only relevant for
184+
/// closures, generators, and inline consts. In that
185+
/// case, it refers to regions that are free in the type
185186
/// -- basically, something bound in the surrounding context.
186187
///
187188
/// Consider this example:
@@ -198,8 +199,8 @@ pub enum RegionClassification {
198199
/// Here, the lifetimes `'a` and `'b` would be **external** to the
199200
/// closure.
200201
///
201-
/// If we are not analyzing a closure, there are no external
202-
/// lifetimes.
202+
/// If we are not analyzing a closure/generator/inline-const,
203+
/// there are no external lifetimes.
203204
External,
204205

205206
/// A **local** lifetime is one about which we know the full set
@@ -424,22 +425,30 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
424425

425426
let typeck_root_def_id = self.infcx.tcx.typeck_root_def_id(self.mir_def.did.to_def_id());
426427

427-
// If this is a closure or generator, then the late-bound regions from the enclosing
428-
// function are actually external regions to us. For example, here, 'a is not local
429-
// to the closure c (although it is local to the fn foo):
430-
// fn foo<'a>() {
431-
// let c = || { let x: &'a u32 = ...; }
432-
// }
433-
if self.mir_def.did.to_def_id() != typeck_root_def_id {
428+
// If this is is a 'root' body (not a closure/generator/inline const), then
429+
// there are no extern regions, so the local regions start at the same
430+
// position as the (empty) sub-list of extern regions
431+
let first_local_index = if self.mir_def.did.to_def_id() == typeck_root_def_id {
432+
first_extern_index
433+
} else {
434+
// If this is a closure, generator, or inline-const, then the late-bound regions from the enclosing
435+
// function are actually external regions to us. For example, here, 'a is not local
436+
// to the closure c (although it is local to the fn foo):
437+
// fn foo<'a>() {
438+
// let c = || { let x: &'a u32 = ...; }
439+
// }
434440
self.infcx
435-
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices)
436-
}
437-
438-
let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
441+
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices);
442+
// Any regions created during the execution of `defining_ty` or during the above
443+
// late-bound region replacement are all considered 'extern' regions
444+
self.infcx.num_region_vars()
445+
};
439446

440447
// "Liberate" the late-bound regions. These correspond to
441448
// "local" free regions.
442-
let first_local_index = self.infcx.num_region_vars();
449+
450+
let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
451+
443452
let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars(
444453
FR,
445454
self.mir_def.did,

src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
| Free Region Mapping
44
| '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r]
5-
| '_#1r | External | ['_#1r, '_#4r]
6-
| '_#2r | External | ['_#2r, '_#1r, '_#4r]
5+
| '_#1r | Local | ['_#1r, '_#4r]
6+
| '_#2r | Local | ['_#2r, '_#1r, '_#4r]
77
| '_#3r | Local | ['_#4r, '_#3r]
88
| '_#4r | Local | ['_#4r]
99
|

src/test/ui/c-variadic/variadic-ffi-4.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f
2020
| |
2121
| lifetime `'f` defined here
2222
LL | ap
23-
| ^^ returning this value requires that `'1` must outlive `'f`
23+
| ^^ function was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1`
2424
|
2525
= note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant
2626
= note: the struct VaListImpl<'f> is invariant over the parameter 'f

src/test/ui/fn/implied-bounds-unnorm-associated-type.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str {
66
| |
77
| lifetime `'a` defined here
88
LL | s
9-
| ^ returning this value requires that `'b` must outlive `'a`
9+
| ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
1010
|
1111
= help: consider adding the following bound: `'b: 'a`
1212

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
error[E0521]: borrowed data escapes outside of associated function
1+
error: lifetime may not live long enough
22
--> $DIR/issue-16683.rs:4:9
33
|
44
LL | trait T<'a> {
55
| -- lifetime `'a` defined here
66
LL | fn a(&'a self) -> &'a bool;
77
LL | fn b(&self) {
8-
| -----
9-
| |
10-
| `self` is a reference that is only valid in the associated function body
11-
| let's call the lifetime of this reference `'1`
8+
| - let's call the lifetime of this reference `'1`
129
LL | self.a();
13-
| ^^^^^^^^
14-
| |
15-
| `self` escapes the associated function body here
16-
| argument requires that `'1` must outlive `'a`
10+
| ^^^^^^^^ argument requires that `'1` must outlive `'a`
1711

1812
error: aborting due to previous error
1913

20-
For more information about this error, try `rustc --explain E0521`.
+3-10
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
1-
error[E0521]: borrowed data escapes outside of associated function
1+
error: lifetime may not live long enough
22
--> $DIR/issue-17758.rs:7:9
33
|
44
LL | trait Foo<'a> {
55
| -- lifetime `'a` defined here
66
LL | fn foo(&'a self);
77
LL | fn bar(&self) {
8-
| -----
9-
| |
10-
| `self` is a reference that is only valid in the associated function body
11-
| let's call the lifetime of this reference `'1`
8+
| - let's call the lifetime of this reference `'1`
129
LL | self.foo();
13-
| ^^^^^^^^^^
14-
| |
15-
| `self` escapes the associated function body here
16-
| argument requires that `'1` must outlive `'a`
10+
| ^^^^^^^^^^ argument requires that `'1` must outlive `'a`
1711

1812
error: aborting due to previous error
1913

20-
For more information about this error, try `rustc --explain E0521`.

src/test/ui/issues/issue-52213.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
77
| lifetime `'a` defined here
88
LL | match (&t,) {
99
LL | ((u,),) => u,
10-
| ^ returning this value requires that `'a` must outlive `'b`
10+
| ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
1111
|
1212
= help: consider adding the following bound: `'a: 'b`
1313

src/test/ui/match/match-ref-mut-invariance.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | impl<'b> S<'b> {
66
LL | fn bar<'a>(&'a mut self) -> &'a mut &'a i32 {
77
| -- lifetime `'a` defined here
88
LL | match self.0 { ref mut x => x }
9-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'b`
9+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
1010
|
1111
= help: consider adding the following bound: `'a: 'b`
1212
= note: requirement occurs because of a mutable reference to &i32

src/test/ui/match/match-ref-mut-let-invariance.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | fn bar<'a>(&'a mut self) -> &'a mut &'a i32 {
77
| -- lifetime `'a` defined here
88
LL | let ref mut x = self.0;
99
LL | x
10-
| ^ returning this value requires that `'a` must outlive `'b`
10+
| ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
1111
|
1212
= help: consider adding the following bound: `'a: 'b`
1313
= note: requirement occurs because of a mutable reference to &i32

src/test/ui/nll/issue-52113.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | fn produce_err<'a, 'b: 'a>(data: &'b mut Vec<&'b u32>, value: &'a u32) -> i
77
| lifetime `'a` defined here
88
...
99
LL | x
10-
| ^ returning this value requires that `'a` must outlive `'b`
10+
| ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
1111
|
1212
= help: consider adding the following bound: `'a: 'b`
1313

src/test/ui/nll/issue-55394.nll.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | fn new(bar: &mut Bar) -> Self {
66
| |
77
| let's call the lifetime of this reference `'1`
88
LL | Foo { bar }
9-
| ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
9+
| ^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
1010

1111
error: aborting due to previous error
1212

src/test/ui/nll/issue-67007-escaping-data.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ struct Consumer<'tcx>(&'tcx ());
1414

1515
impl<'tcx> Consumer<'tcx> {
1616
fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
17-
let other = self.use_fcx(fcx); //~ ERROR borrowed data
17+
let other = self.use_fcx(fcx); //~ ERROR lifetime may not live long enough
1818
fcx.use_it(other);
1919
}
2020

Original file line numberDiff line numberDiff line change
@@ -1,21 +1,14 @@
1-
error[E0521]: borrowed data escapes outside of associated function
1+
error: lifetime may not live long enough
22
--> $DIR/issue-67007-escaping-data.rs:17:21
33
|
44
LL | impl<'tcx> Consumer<'tcx> {
55
| ---- lifetime `'tcx` defined here
66
LL | fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
7-
| -- ----- --- `fcx` is a reference that is only valid in the associated function body
8-
| | |
9-
| | `self` declared here, outside of the associated function body
10-
| lifetime `'a` defined here
7+
| -- lifetime `'a` defined here
118
LL | let other = self.use_fcx(fcx);
12-
| ^^^^^^^^^^^^^^^^^
13-
| |
14-
| `fcx` escapes the associated function body here
15-
| argument requires that `'a` must outlive `'tcx`
9+
| ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx`
1610
|
1711
= help: consider adding the following bound: `'a: 'tcx`
1812

1913
error: aborting due to previous error
2014

21-
For more information about this error, try `rustc --explain E0521`.

src/test/ui/nll/mir_check_cast_closure.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ LL | fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 {
77
| lifetime `'a` defined here
88
LL | let g: fn(_, _) -> _ = |_x, y| y;
99
LL | g
10-
| ^ returning this value requires that `'b` must outlive `'a`
10+
| ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
1111
|
1212
= help: consider adding the following bound: `'b: 'a`
1313

src/test/ui/nll/outlives-suggestion-more.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usiz
66
| |
77
| lifetime `'a` defined here
88
LL | (x, y)
9-
| ^^^^^^ returning this value requires that `'a` must outlive `'c`
9+
| ^^^^^^ function was supposed to return data with lifetime `'c` but it is returning data with lifetime `'a`
1010
|
1111
= help: consider adding the following bound: `'a: 'c`
1212

@@ -18,7 +18,7 @@ LL | fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usiz
1818
| |
1919
| lifetime `'b` defined here
2020
LL | (x, y)
21-
| ^^^^^^ returning this value requires that `'b` must outlive `'d`
21+
| ^^^^^^ function was supposed to return data with lifetime `'d` but it is returning data with lifetime `'b`
2222
|
2323
= help: consider adding the following bound: `'b: 'd`
2424

@@ -35,7 +35,7 @@ LL | fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usi
3535
| |
3636
| lifetime `'a` defined here
3737
LL | (x, y)
38-
| ^^^^^^ returning this value requires that `'a` must outlive `'c`
38+
| ^^^^^^ function was supposed to return data with lifetime `'c` but it is returning data with lifetime `'a`
3939
|
4040
= help: consider adding the following bound: `'a: 'c`
4141

src/test/ui/nll/outlives-suggestion-simple.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub struct Foo2<'a> {
7070
impl<'a> Foo2<'a> {
7171
// should not produce outlives suggestions to name 'self
7272
fn get_bar(&self) -> Bar2 {
73-
Bar2::new(&self) //~ERROR borrowed data escapes outside of associated function
73+
Bar2::new(&self) //~ERROR lifetime may not live long enough
7474
}
7575
}
7676

src/test/ui/nll/outlives-suggestion-simple.stderr

+7-15
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize {
66
| |
77
| lifetime `'a` defined here
88
LL | x
9-
| ^ returning this value requires that `'a` must outlive `'b`
9+
| ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
1010
|
1111
= help: consider adding the following bound: `'a: 'b`
1212

@@ -53,7 +53,7 @@ LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) {
5353
| lifetime `'a` defined here
5454
...
5555
LL | (x, x)
56-
| ^^^^^^ returning this value requires that `'a` must outlive `'b`
56+
| ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
5757
|
5858
= help: consider adding the following bound: `'a: 'b`
5959

@@ -73,7 +73,7 @@ LL | impl<'a> Bar<'a> {
7373
LL | pub fn get<'b>(&self) -> &'b usize {
7474
| -- lifetime `'b` defined here
7575
LL | self.x
76-
| ^^^^^^ returning this value requires that `'a` must outlive `'b`
76+
| ^^^^^^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
7777
|
7878
= help: consider adding the following bound: `'a: 'b`
7979

@@ -85,28 +85,20 @@ LL | impl<'a> Baz<'a> {
8585
LL | fn get<'b>(&'b self) -> &'a i32 {
8686
| -- lifetime `'b` defined here
8787
LL | self.x
88-
| ^^^^^^ returning this value requires that `'b` must outlive `'a`
88+
| ^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
8989
|
9090
= help: consider adding the following bound: `'b: 'a`
9191

92-
error[E0521]: borrowed data escapes outside of associated function
92+
error: lifetime may not live long enough
9393
--> $DIR/outlives-suggestion-simple.rs:73:9
9494
|
9595
LL | impl<'a> Foo2<'a> {
9696
| -- lifetime `'a` defined here
9797
LL | // should not produce outlives suggestions to name 'self
9898
LL | fn get_bar(&self) -> Bar2 {
99-
| -----
100-
| |
101-
| `self` declared here, outside of the associated function body
102-
| `self` is a reference that is only valid in the associated function body
103-
| let's call the lifetime of this reference `'1`
99+
| - let's call the lifetime of this reference `'1`
104100
LL | Bar2::new(&self)
105-
| ^^^^^^^^^^^^^^^^
106-
| |
107-
| `self` escapes the associated function body here
108-
| argument requires that `'1` must outlive `'a`
101+
| ^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`
109102

110103
error: aborting due to 9 previous errors
111104

112-
For more information about this error, try `rustc --explain E0521`.

src/test/ui/nll/type-alias-free-regions.nll.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ LL | impl<'a> FromBox<'a> for C<'a> {
66
LL | fn from_box(b: Box<B>) -> Self {
77
| - has type `Box<Box<&'1 isize>>`
88
LL | C { f: b }
9-
| ^^^^^^^^^^ returning this value requires that `'1` must outlive `'a`
9+
| ^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
1010

1111
error: lifetime may not live long enough
1212
--> $DIR/type-alias-free-regions.rs:27:9
@@ -16,7 +16,7 @@ LL | impl<'a> FromTuple<'a> for C<'a> {
1616
LL | fn from_tuple(b: (B,)) -> Self {
1717
| - has type `(Box<&'1 isize>,)`
1818
LL | C { f: Box::new(b.0) }
19-
| ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'a`
19+
| ^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
2020

2121
error: aborting due to 2 previous errors
2222

0 commit comments

Comments
 (0)