Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add "temporary value borrowed for too long" error #54164

Merged
merged 3 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/librustc_mir/borrow_check/error_reporting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -694,9 +694,9 @@ impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {

let tcx = self.infcx.tcx;
let mut err =
tcx.path_does_not_live_long_enough(proper_span, "borrowed value", Origin::Mir);
err.span_label(proper_span, "temporary value does not live long enough");
err.span_label(drop_span, "temporary value only lives until here");
tcx.temporary_value_borrowed_for_too_long(proper_span, Origin::Mir);
err.span_label(proper_span, "creates a temporary which is freed while still in use");
err.span_label(drop_span, "temporary value is freed at the end of this statement");

let explanation = self.explain_why_borrow_contains_point(context, borrow, None);
match explanation {
Expand Down
85 changes: 85 additions & 0 deletions src/librustc_mir/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2261,7 +2261,92 @@ that after `demo` finishes excuting, something else (such as the
destructor!) could access `s.data` after the end of that shorter
lifetime, which would again violate the `&mut`-borrow's exclusive
access.
"##,

E0716: r##"
This error indicates that a temporary value is being dropped
while a borrow is still in active use.

Erroneous code example:

```compile_fail,E0716
# #![feature(nll)]
fn foo() -> i32 { 22 }
fn bar(x: &i32) -> &i32 { x }
let p = bar(&foo());
// ------ creates a temporary
let q = *p;
```

Here, the expression `&foo()` is borrowing the expression
`foo()`. As `foo()` is call to a function, and not the name of
a variable, this creates a **temporary** -- that temporary stores
the return value from `foo()` so that it can be borrowed.
So you might imagine that `let p = bar(&foo())` is equivalent
to this:

```compile_fail,E0597
# fn foo() -> i32 { 22 }
# fn bar(x: &i32) -> &i32 { x }
let p = {
let tmp = foo(); // the temporary
bar(&tmp)
}; // <-- tmp is freed as we exit this block
let q = p;
```

Whenever a temporary is created, it is automatically dropped (freed)
according to fixed rules. Ordinarily, the temporary is dropped
at the end of the enclosing statement -- in this case, after the `let`.
This is illustrated in the example above by showing that `tmp` would
be freed as we exit the block.

To fix this problem, you need to create a local variable
to store the value in rather than relying on a temporary.
For example, you might change the original program to
the following:

```
fn foo() -> i32 { 22 }
fn bar(x: &i32) -> &i32 { x }
let value = foo(); // dropped at the end of the enclosing block
let p = bar(&value);
let q = *p;
```

By introducing the explicit `let value`, we allocate storage
that will last until the end of the enclosing block (when `value`
goes out of scope). When we borrow `&value`, we are borrowing a
local variable that already exists, and hence no temporary is created.

Temporaries are not always dropped at the end of the enclosing
statement. In simple cases where the `&` expression is immediately
stored into a variable, the compiler will automatically extend
the lifetime of the temporary until the end of the enclosinb
block. Therefore, an alternative way to fix the original
program is to write `let tmp = &foo()` and not `let tmp = foo()`:

```
fn foo() -> i32 { 22 }
fn bar(x: &i32) -> &i32 { x }
let value = &foo();
let p = bar(value);
let q = *p;
```

Here, we are still borrowing `foo()`, but as the borrow is assigned
directly into a variable, the temporary will not be dropped until
the end of the enclosing block. Similar rules apply when temporaries
are stored into aggregate structures like a tuple or struct:

```
// Here, two temporaries are created, but
// as they are stored directly into `value`,
// they are not dropped until the end of the
// enclosing block.
fn foo() -> i32 { 22 }
let value = (&foo(), &foo());
```
"##,

}
Expand Down
16 changes: 16 additions & 0 deletions src/librustc_mir/util/borrowck_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,6 +730,22 @@ pub trait BorrowckErrors<'cx>: Sized + Copy {

self.cancel_if_wrong_origin(err, o)
}

fn temporary_value_borrowed_for_too_long(
self,
span: Span,
o: Origin,
) -> DiagnosticBuilder<'cx> {
let err = struct_span_err!(
self,
span,
E0716,
"temporary value dropped while borrowed{OGN}",
OGN = o
);

self.cancel_if_wrong_origin(err, o)
}
}

impl<'cx, 'gcx, 'tcx> BorrowckErrors<'cx> for TyCtxt<'cx, 'gcx, 'tcx> {
Expand Down
18 changes: 18 additions & 0 deletions src/test/ui/borrowck/borrowck-borrow-from-temporary.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/borrowck-borrow-from-temporary.rs:19:24
|
LL | let &Foo(ref x) = &id(Foo(3)); //~ ERROR borrowed value does not live long enough
| ^^^^^^^^^^ creates a temporary which is freed while still in use
LL | x
LL | }
| - temporary value is freed at the end of this statement
|
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 18:8...
--> $DIR/borrowck-borrow-from-temporary.rs:18:8
|
LL | fn foo<'a>() -> &'a isize {
| ^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0716`.
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/borrowck-borrowed-uniq-rvalue-2.rs:32:20
|
LL | let x = defer(&vec!["Goodbye", "world!"]);
| ^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value only lives until here
| ^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| temporary value does not live long enough
| creates a temporary which is freed while still in use
LL | x.x[0];
| ------ borrow later used here
|
Expand All @@ -13,4 +13,4 @@ LL | x.x[0];

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
For more information about this error, try `rustc --explain E0716`.
8 changes: 4 additions & 4 deletions src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/borrowck-borrowed-uniq-rvalue.rs:20:28
|
LL | buggy_map.insert(42, &*Box::new(1)); //~ ERROR borrowed value does not live long enough
| ^^^^^^^^^^^ - temporary value only lives until here
| ^^^^^^^^^^^ - temporary value is freed at the end of this statement
| |
| temporary value does not live long enough
| creates a temporary which is freed while still in use
...
LL | buggy_map.insert(43, &*tmp);
| --------- borrow later used here
Expand All @@ -13,4 +13,4 @@ LL | buggy_map.insert(43, &*tmp);

error: aborting due to previous error

For more information about this error, try `rustc --explain E0597`.
For more information about this error, try `rustc --explain E0716`.
32 changes: 16 additions & 16 deletions src/test/ui/borrowck/promote-ref-mut-in-let-issue-46557.nll.stderr
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:15:21
|
LL | let ref mut x = 1234543; //~ ERROR
| ^^^^^^^ temporary value does not live long enough
| ^^^^^^^ creates a temporary which is freed while still in use
LL | x
LL | }
| - temporary value only lives until here
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:20:25
|
LL | let (ref mut x, ) = (1234543, ); //~ ERROR
| ^^^^^^^^^^^ temporary value does not live long enough
| ^^^^^^^^^^^ creates a temporary which is freed while still in use
LL | x
LL | }
| - temporary value only lives until here
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:25:11
|
LL | match 1234543 {
| ^^^^^^^ temporary value does not live long enough
| ^^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value only lives until here
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:31:11
|
LL | match (123443,) {
| ^^^^^^^^^ temporary value does not live long enough
| ^^^^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value only lives until here
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0597]: borrowed value does not live long enough
error[E0716]: temporary value dropped while borrowed
--> $DIR/promote-ref-mut-in-let-issue-46557.rs:37:10
|
LL | &mut 1234543 //~ ERROR
| ^^^^^^^ temporary value does not live long enough
| ^^^^^^^ creates a temporary which is freed while still in use
LL | }
| - temporary value only lives until here
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to 5 previous errors

For more information about this error, try `rustc --explain E0597`.
For more information about this error, try `rustc --explain E0716`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
error: `foo` is not yet stable as a const fn
--> $DIR/dont_promote_unstable_const_fn.rs:25:25
|
LL | const fn bar() -> u32 { foo() } //~ ERROR `foo` is not yet stable as a const fn
| ^^^^^
|
= help: in Nightly builds, add `#![feature(foo)]` to the crate attributes to enable

error[E0716]: temporary value dropped while borrowed
--> $DIR/dont_promote_unstable_const_fn.rs:28:28
|
LL | let _: &'static u32 = &foo(); //~ ERROR does not live long enough
| ^^^^^ creates a temporary which is freed while still in use
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0716]: temporary value dropped while borrowed
--> $DIR/dont_promote_unstable_const_fn.rs:32:28
|
LL | let _: &'static u32 = &meh(); //~ ERROR does not live long enough
| ^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0716]: temporary value dropped while borrowed
--> $DIR/dont_promote_unstable_const_fn.rs:33:26
|
LL | let x: &'static _ = &std::time::Duration::from_millis(42).subsec_millis();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
LL | //~^ does not live long enough
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0716`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:18:28
|
LL | let _: &'static u32 = &foo(); //~ ERROR does not live long enough
| ^^^^^ creates a temporary which is freed while still in use
LL | let _x: &'static u32 = &foo(); //~ ERROR does not live long enough
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0716]: temporary value dropped while borrowed
--> $DIR/dont_promote_unstable_const_fn_cross_crate.rs:19:29
|
LL | let _x: &'static u32 = &foo(); //~ ERROR does not live long enough
| ^^^^^ creates a temporary which is freed while still in use
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0716`.
35 changes: 35 additions & 0 deletions src/test/ui/consts/const-eval/promoted_raw_ptr_ops.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_raw_ptr_ops.rs:14:29
|
LL | let x: &'static bool = &(42 as *const i32 == 43 as *const i32);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
...
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_raw_ptr_ops.rs:16:30
|
LL | let y: &'static usize = &(&1 as *const i32 as usize + 1); //~ ERROR does not live long enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) }); //~ ERROR does not live long enough
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error[E0716]: temporary value dropped while borrowed
--> $DIR/promoted_raw_ptr_ops.rs:17:28
|
LL | let z: &'static i32 = &(unsafe { *(42 as *const i32) }); //~ ERROR does not live long enough
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0716`.
14 changes: 14 additions & 0 deletions src/test/ui/consts/const-eval/transmute-const-promotion.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/transmute-const-promotion.rs:16:37
|
LL | let x: &'static u32 = unsafe { &mem::transmute(3.0f32) };
| ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use
LL | //~^ ERROR value does not live long enough
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to previous error

For more information about this error, try `rustc --explain E0716`.
16 changes: 16 additions & 0 deletions src/test/ui/consts/const-eval/union_promotion.nll.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/union_promotion.rs:19:29
|
LL | let x: &'static bool = &unsafe { //~ borrowed value does not live long enough
| _____________________________^
LL | | Foo { a: &1 }.b == Foo { a: &2 }.b
LL | | };
| |_____^ creates a temporary which is freed while still in use
LL | }
| - temporary value is freed at the end of this statement
|
= note: borrowed value must be valid for the static lifetime...

error: aborting due to previous error

For more information about this error, try `rustc --explain E0716`.
Loading