2014-05-21 23:44:49 -05:00
|
|
|
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
fn f() {
|
2015-01-31 10:23:42 -06:00
|
|
|
let x = [1].iter(); //~ ERROR borrowed value does not live long enough
|
Add `CodeExtent::Remainder` variant; pre-req for new scoping/drop rules.
This new variant introduces finer-grain code extents, i.e. we now
track that a binding lives only for a suffix of a block, and
(importantly) will be dropped when it goes out of scope *before* the
bindings that occurred earlier in the block.
Both of these notions are neatly captured by marking the block (and
each suffix) as an enclosing scope of the next suffix beneath it.
This is work that is part of the foundation for issue #8861.
(It actually has been seen in earlier posted pull requests; I have
just factored it out into its own PR to ease my own rebasing.)
----
These finer grained scopes do mean that some code is newly rejected by
`rustc`; for example:
```rust
let mut map : HashMap<u8, &u8> = HashMap::new();
let tmp = Box::new(2);
map.insert(43, &*tmp);
```
This will now fail to compile with a message that `*tmp` does not live
long enough, because the scope of `tmp` is now strictly smaller than
that of `map`, and the use of `&u8` in map's type requires that the
borrowed references are all to data that live at least as long as the
map.
The usual fix for a case like this is to move the binding for `tmp`
up above that of `map`; note that you can still leave the initialization
in the original spot, like so:
```rust
let tmp;
let mut map : HashMap<u8, &u8> = HashMap::new();
tmp = box 2;
map.insert(43, &*tmp);
```
Similarly, one can encounter an analogous situation with `Vec`: one
would need to rewrite:
```rust
let mut vec = Vec::new();
let tmp = 'c';
vec.push(&tmp);
```
as:
```
let tmp;
let mut vec = Vec::new();
tmp = 'c';
vec.push(&tmp);
```
----
In some corner cases, it does not suffice to reorder the bindings; in
particular, when the types for both bindings need to reflect exactly
the *same* code extent, and a parent/child relationship between them
does not work.
In pnkfelix's experience this has arisen most often when mixing uses
of cyclic data structures while also allowing a lifetime parameter
`'a` to flow into a type parameter context where the type is
*invariant* with respect to the type parameter. An important instance
of this is `arena::TypedArena<T>`, which is invariant with respect
to `T`.
(The reason that variance is relevant is this: *if* `TypedArena` were
covariant with respect to its type parameter, then we could assign it
the longer lifetime when it is initialized, and then convert it to a
subtype (via covariance) with a shorter lifetime when necessary. But
`TypedArena` is invariant with respect to its type parameter, and thus
if `S` is a subtype of `T` (in particular, if `S` has a lifetime
parameter that is shorter than that of `T`), then a `TypedArena<S>` is
unrelated to `TypedArena<T>`.)
Concretely, consider code like this:
```rust
struct Node<'a> { sibling: Option<&'a Node<'a>> }
struct Context<'a> {
// because of this field, `Context<'a>` is invariant with respect to `'a`.
arena: &'a TypedArena<Node<'a>>,
...
}
fn new_ctxt<'a>(arena: &'a TypedArena<Node<'a>>) -> Context<'a> { ... }
fn use_ctxt<'a>(fcx: &'a Context<'a>) { ... }
let arena = TypedArena::new();
let ctxt = new_ctxt(&arena);
use_ctxt(&ctxt);
```
In these situations, if you try to introduce two bindings via two
distinct `let` statements, each is (with this commit) assigned a
distinct extent, and the region inference system cannot find a single
region to assign to the lifetime `'a` that works for both of the
bindings. So you get an error that `ctxt` does not live long enough;
but moving its binding up above that of `arena` just shifts the error
so now the compiler complains that `arena` does not live long enough.
SO: What to do? The easiest fix in this case is to ensure that the two
bindings *do* get assigned the same static extent, by stuffing both
bindings into the same let statement, like so:
```rust
let (arena, ctxt): (TypedArena, Context);
arena = TypedArena::new();
ctxt = new_ctxt(&arena);
use_ctxt(&ctxt);
```
Due to the new code rejections outlined above, this is a ...
[breaking-change]
2015-01-26 05:48:19 -06:00
|
|
|
//~^ NOTE reference must be valid for the block suffix following statement
|
2014-10-17 21:39:44 -05:00
|
|
|
//~^^ HELP consider using a `let` binding to increase its lifetime
|
2014-05-21 23:44:49 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
f();
|
|
|
|
}
|