Skip to content

Commit

Permalink
Rollup merge of #92467 - Aaron1011:extern-local-region, r=oli-obk
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
matthiaskrgr authored Jan 21, 2022
2 parents fc69406 + e3a048c commit 10c9ec3
Show file tree
Hide file tree
Showing 38 changed files with 98 additions and 124 deletions.
41 changes: 25 additions & 16 deletions compiler/rustc_borrowck/src/universal_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ pub enum RegionClassification {
/// anywhere. There is only one, `'static`.
Global,

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

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

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

// If this is a closure or generator, then the late-bound regions from the enclosing
// function are actually external regions to us. For example, here, 'a is not local
// to the closure c (although it is local to the fn foo):
// fn foo<'a>() {
// let c = || { let x: &'a u32 = ...; }
// }
if self.mir_def.did.to_def_id() != typeck_root_def_id {
// If this is is a 'root' body (not a closure/generator/inline const), then
// there are no extern regions, so the local regions start at the same
// position as the (empty) sub-list of extern regions
let first_local_index = if self.mir_def.did.to_def_id() == typeck_root_def_id {
first_extern_index
} else {
// If this is a closure, generator, or inline-const, then the late-bound regions from the enclosing
// function are actually external regions to us. For example, here, 'a is not local
// to the closure c (although it is local to the fn foo):
// fn foo<'a>() {
// let c = || { let x: &'a u32 = ...; }
// }
self.infcx
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices)
}

let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);
.replace_late_bound_regions_with_nll_infer_vars(self.mir_def.did, &mut indices);
// Any regions created during the execution of `defining_ty` or during the above
// late-bound region replacement are all considered 'extern' regions
self.infcx.num_region_vars()
};

// "Liberate" the late-bound regions. These correspond to
// "local" free regions.
let first_local_index = self.infcx.num_region_vars();

let bound_inputs_and_output = self.compute_inputs_and_output(&indices, defining_ty);

let inputs_and_output = self.infcx.replace_bound_regions_with_nll_infer_vars(
FR,
self.mir_def.did,
Expand Down
4 changes: 2 additions & 2 deletions src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

| Free Region Mapping
| '_#0r | Global | ['_#2r, '_#1r, '_#0r, '_#4r, '_#3r]
| '_#1r | External | ['_#1r, '_#4r]
| '_#2r | External | ['_#2r, '_#1r, '_#4r]
| '_#1r | Local | ['_#1r, '_#4r]
| '_#2r | Local | ['_#2r, '_#1r, '_#4r]
| '_#3r | Local | ['_#4r, '_#3r]
| '_#4r | Local | ['_#4r]
|
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/c-variadic/variadic-ffi-4.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ LL | pub unsafe extern "C" fn no_escape0<'f>(_: usize, ap: ...) -> VaListImpl<'f
| |
| lifetime `'f` defined here
LL | ap
| ^^ returning this value requires that `'1` must outlive `'f`
| ^^ function was supposed to return data with lifetime `'f` but it is returning data with lifetime `'1`
|
= note: requirement occurs because of the type VaListImpl<'_>, which makes the generic argument '_ invariant
= note: the struct VaListImpl<'f> is invariant over the parameter 'f
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | fn f<'a, 'b>(s: &'b str, _: <&'a &'b () as Trait>::Type) -> &'a str {
| |
| lifetime `'a` defined here
LL | s
| ^ returning this value requires that `'b` must outlive `'a`
| ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`

Expand Down
13 changes: 3 additions & 10 deletions src/test/ui/issues/issue-16683.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
error[E0521]: borrowed data escapes outside of associated function
error: lifetime may not live long enough
--> $DIR/issue-16683.rs:4:9
|
LL | trait T<'a> {
| -- lifetime `'a` defined here
LL | fn a(&'a self) -> &'a bool;
LL | fn b(&self) {
| -----
| |
| `self` is a reference that is only valid in the associated function body
| let's call the lifetime of this reference `'1`
| - let's call the lifetime of this reference `'1`
LL | self.a();
| ^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'1` must outlive `'a`
| ^^^^^^^^ argument requires that `'1` must outlive `'a`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0521`.
13 changes: 3 additions & 10 deletions src/test/ui/issues/issue-17758.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,20 +1,13 @@
error[E0521]: borrowed data escapes outside of associated function
error: lifetime may not live long enough
--> $DIR/issue-17758.rs:7:9
|
LL | trait Foo<'a> {
| -- lifetime `'a` defined here
LL | fn foo(&'a self);
LL | fn bar(&self) {
| -----
| |
| `self` is a reference that is only valid in the associated function body
| let's call the lifetime of this reference `'1`
| - let's call the lifetime of this reference `'1`
LL | self.foo();
| ^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'1` must outlive `'a`
| ^^^^^^^^^^ argument requires that `'1` must outlive `'a`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0521`.
2 changes: 1 addition & 1 deletion src/test/ui/issues/issue-52213.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
| lifetime `'a` defined here
LL | match (&t,) {
LL | ((u,),) => u,
| ^ returning this value requires that `'a` must outlive `'b`
| ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/match/match-ref-mut-invariance.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | impl<'b> S<'b> {
LL | fn bar<'a>(&'a mut self) -> &'a mut &'a i32 {
| -- lifetime `'a` defined here
LL | match self.0 { ref mut x => x }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'b`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a mutable reference to &i32
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/match/match-ref-mut-let-invariance.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | fn bar<'a>(&'a mut self) -> &'a mut &'a i32 {
| -- lifetime `'a` defined here
LL | let ref mut x = self.0;
LL | x
| ^ returning this value requires that `'a` must outlive `'b`
| ^ associated function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`
= note: requirement occurs because of a mutable reference to &i32
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/issue-52113.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | fn produce_err<'a, 'b: 'a>(data: &'b mut Vec<&'b u32>, value: &'a u32) -> i
| lifetime `'a` defined here
...
LL | x
| ^ returning this value requires that `'a` must outlive `'b`
| ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/issue-55394.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | fn new(bar: &mut Bar) -> Self {
| |
| let's call the lifetime of this reference `'1`
LL | Foo { bar }
| ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| ^^^^^^^^^^^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`

error: aborting due to previous error

2 changes: 1 addition & 1 deletion src/test/ui/nll/issue-67007-escaping-data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct Consumer<'tcx>(&'tcx ());

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

Expand Down
13 changes: 3 additions & 10 deletions src/test/ui/nll/issue-67007-escaping-data.stderr
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
error[E0521]: borrowed data escapes outside of associated function
error: lifetime may not live long enough
--> $DIR/issue-67007-escaping-data.rs:17:21
|
LL | impl<'tcx> Consumer<'tcx> {
| ---- lifetime `'tcx` defined here
LL | fn bad_method<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
| -- ----- --- `fcx` is a reference that is only valid in the associated function body
| | |
| | `self` declared here, outside of the associated function body
| lifetime `'a` defined here
| -- lifetime `'a` defined here
LL | let other = self.use_fcx(fcx);
| ^^^^^^^^^^^^^^^^^
| |
| `fcx` escapes the associated function body here
| argument requires that `'a` must outlive `'tcx`
| ^^^^^^^^^^^^^^^^^ argument requires that `'a` must outlive `'tcx`
|
= help: consider adding the following bound: `'a: 'tcx`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0521`.
2 changes: 1 addition & 1 deletion src/test/ui/nll/mir_check_cast_closure.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | fn bar<'a, 'b>() -> fn(&'a u32, &'b u32) -> &'a u32 {
| lifetime `'a` defined here
LL | let g: fn(_, _) -> _ = |_x, y| y;
LL | g
| ^ returning this value requires that `'b` must outlive `'a`
| ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
|
= help: consider adding the following bound: `'b: 'a`

Expand Down
6 changes: 3 additions & 3 deletions src/test/ui/nll/outlives-suggestion-more.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usiz
| |
| lifetime `'a` defined here
LL | (x, y)
| ^^^^^^ returning this value requires that `'a` must outlive `'c`
| ^^^^^^ function was supposed to return data with lifetime `'c` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'c`

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

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

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/nll/outlives-suggestion-simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub struct Foo2<'a> {
impl<'a> Foo2<'a> {
// should not produce outlives suggestions to name 'self
fn get_bar(&self) -> Bar2 {
Bar2::new(&self) //~ERROR borrowed data escapes outside of associated function
Bar2::new(&self) //~ERROR lifetime may not live long enough
}
}

Expand Down
22 changes: 7 additions & 15 deletions src/test/ui/nll/outlives-suggestion-simple.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize {
| |
| lifetime `'a` defined here
LL | x
| ^ returning this value requires that `'a` must outlive `'b`
| ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`

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

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

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

error[E0521]: borrowed data escapes outside of associated function
error: lifetime may not live long enough
--> $DIR/outlives-suggestion-simple.rs:73:9
|
LL | impl<'a> Foo2<'a> {
| -- lifetime `'a` defined here
LL | // should not produce outlives suggestions to name 'self
LL | fn get_bar(&self) -> Bar2 {
| -----
| |
| `self` declared here, outside of the associated function body
| `self` is a reference that is only valid in the associated function body
| let's call the lifetime of this reference `'1`
| - let's call the lifetime of this reference `'1`
LL | Bar2::new(&self)
| ^^^^^^^^^^^^^^^^
| |
| `self` escapes the associated function body here
| argument requires that `'1` must outlive `'a`
| ^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'a`

error: aborting due to 9 previous errors

For more information about this error, try `rustc --explain E0521`.
4 changes: 2 additions & 2 deletions src/test/ui/nll/type-alias-free-regions.nll.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ LL | impl<'a> FromBox<'a> for C<'a> {
LL | fn from_box(b: Box<B>) -> Self {
| - has type `Box<Box<&'1 isize>>`
LL | C { f: b }
| ^^^^^^^^^^ returning this value requires that `'1` must outlive `'a`
| ^^^^^^^^^^ associated function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`

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

error: aborting due to 2 previous errors

Loading

0 comments on commit 10c9ec3

Please sign in to comment.