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

tracking issue for lifetime inference error work (E0495) #42516

Open
nikomatsakis opened this Issue Jun 7, 2017 · 10 comments

Comments

Projects
None yet
8 participants
@nikomatsakis
Contributor

nikomatsakis commented Jun 7, 2017

This is a general tracking issue for the work on improving lifetime errors (in particular the "cannot infer lifetime" error E0495, but also including other errors). This is a rather large project, since there are many different classes of errors, so part of the work is finding scenarios and making more targeted error messages for them.

In general, I am trying to track errors and brainstorm here. There is also an etherpad where I have done some note-taking.

Current road-map

The plan is to have three major kinds of errors, delivered in order of preference. The basic work here is done, but there remain gaps to be covered. If you're interested in helping out, check out the issues associated with the unchecked check boxes!

  • "explicit lifetime required" (example)
    • these are cases where the user has an explicit lifetime name, but they need to use it in more places
    • #42700 -- we don't handle the case where the anonymous lifetime appears in self very well
  • #42703 -- "this parameter and the return type are declared with different lifetimes..." (#44124)
  • "these two types are declared with different lifetimes..." (example)
    • This is a sort of fallback. It covers all kinds of cases.
    • #44508 -- it doesn't work on lifetimes in structs
    • In particular, the goal with the base message is just to describe what the problem is without suggesting a particular solution. We then want to consider various extensions where we can propose a fix:
      • Suggest a new signature (#44506)
      • Suggest adding a where clause when there are already two named lifetimes (#44507)
      • We have to be careful in the above cases around methods appearing in an impl of a trait, since the user may not have freedom to change the signature. Here are some cases:
        • #42706 -- impl does not use the same signature as declared in the trait
  • Cross-cutting concerns:
    • Handle SubSup-style errors and not just concrete failures (#42701)
    • #43276 -- double-check behavior in case with more binders
  • Higher-ranked closure error messages should avoid "anonymous lifetime #2 defined on the body" #45983
  • smarter blame assignment for lifetime inference errors #45979

Examples in need of analysis

  • #42574 -- weird interaction with closures and &'static arguments

Future plans

  • Improve how lifetime inference selects which edges in the region inference graph are at fault. The code for finding the regions that conflict seems to work quite well -- but the code to select which edges to blame is hopeless. I think we need to look specifically for cases where the user "puts things in tension" (e.g., foo(a) puts the formal declarations of foo in tension with the type of a). I'll try to write up these thoughts in a bit more detail later.

Other issues

Here are various issues that I have subsumed into this one. These are a checklist because I want to go over them and extract any key examples into this issue and then close them. Feel free to do so (in the comments).

bors added a commit that referenced this issue Jun 28, 2017

Auto merge of #42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter

This is a fix for #42517
Note that this only handles the above case for **function declarations** and **traits**.
`impl items` and `closures` will be handled in a later PR.
Example
```
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
    if x > y { x } else { y }
}
```
now displays the following error message. ui tests have been added for the same.
```
error[E0611]: explicit lifetime required in the type of `x`
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
   |                     ^ consider changing the type of `x` to `&'a i32`
12 |     if x > y { x } else { y }
   |                  - lifetime `'a` required
```
#42516
r? @nikomatsakis

frewsxcv added a commit to frewsxcv/rust that referenced this issue Jun 29, 2017

Rollup merge of rust-lang#42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter

This is a fix for rust-lang#42517
Note that this only handles the above case for **function declarations** and **traits**.
`impl items` and `closures` will be handled in a later PR.
Example
```
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
    if x > y { x } else { y }
}
```
now displays the following error message. ui tests have been added for the same.
```
error[E0611]: explicit lifetime required in the type of `x`
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
   |                     ^ consider changing the type of `x` to `&'a i32`
12 |     if x > y { x } else { y }
   |                  - lifetime `'a` required
```
rust-lang#42516
r? @nikomatsakis

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jun 30, 2017

Rollup merge of rust-lang#42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter

This is a fix for rust-lang#42517
Note that this only handles the above case for **function declarations** and **traits**.
`impl items` and `closures` will be handled in a later PR.
Example
```
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
    if x > y { x } else { y }
}
```
now displays the following error message. ui tests have been added for the same.
```
error[E0611]: explicit lifetime required in the type of `x`
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
   |                     ^ consider changing the type of `x` to `&'a i32`
12 |     if x > y { x } else { y }
   |                  - lifetime `'a` required
```
rust-lang#42516
r? @nikomatsakis

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jun 30, 2017

Rollup merge of rust-lang#42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter

This is a fix for rust-lang#42517
Note that this only handles the above case for **function declarations** and **traits**.
`impl items` and `closures` will be handled in a later PR.
Example
```
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
    if x > y { x } else { y }
}
```
now displays the following error message. ui tests have been added for the same.
```
error[E0611]: explicit lifetime required in the type of `x`
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
   |                     ^ consider changing the type of `x` to `&'a i32`
12 |     if x > y { x } else { y }
   |                  - lifetime `'a` required
```
rust-lang#42516
r? @nikomatsakis

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jun 30, 2017

Rollup merge of rust-lang#42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter

This is a fix for rust-lang#42517
Note that this only handles the above case for **function declarations** and **traits**.
`impl items` and `closures` will be handled in a later PR.
Example
```
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
    if x > y { x } else { y }
}
```
now displays the following error message. ui tests have been added for the same.
```
error[E0611]: explicit lifetime required in the type of `x`
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
   |                     ^ consider changing the type of `x` to `&'a i32`
12 |     if x > y { x } else { y }
   |                  - lifetime `'a` required
```
rust-lang#42516
r? @nikomatsakis

GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this issue Jun 30, 2017

Rollup merge of rust-lang#42669 - gaurikholkar:master, r=nikomatsakis
Adding diagnostic code 0611 for lifetime errors with one named, one anonymous lifetime parameter

This is a fix for rust-lang#42517
Note that this only handles the above case for **function declarations** and **traits**.
`impl items` and `closures` will be handled in a later PR.
Example
```
fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
    if x > y { x } else { y }
}
```
now displays the following error message. ui tests have been added for the same.
```
error[E0611]: explicit lifetime required in the type of `x`
11 | fn foo<'a>(x: &i32, y: &'a i32) -> &'a i32 {
   |                     ^ consider changing the type of `x` to `&'a i32`
12 |     if x > y { x } else { y }
   |                  - lifetime `'a` required
```
rust-lang#42516
r? @nikomatsakis
@cengizIO

This comment has been minimized.

Show comment
Hide comment
@cengizIO

cengizIO Jul 17, 2017

Contributor

@nikomatsakis

I'd like to take at least one of these and help!

Contributor

cengizIO commented Jul 17, 2017

@nikomatsakis

I'd like to take at least one of these and help!

@gaurikholkar

This comment has been minimized.

Show comment
Hide comment
@gaurikholkar
Contributor

gaurikholkar commented Jul 17, 2017

@gaurikholkar

This comment has been minimized.

Show comment
Hide comment
@gaurikholkar
Contributor

gaurikholkar commented Jul 23, 2017

@Vurich

This comment has been minimized.

Show comment
Hide comment
@Vurich

Vurich Aug 23, 2017

I was referred here for a comment I made about how lifetime errors involving complex elision should print the fully elided parameters. By complex elision I mean elision that doesn't just result in the same lifetime for every param, so that's elision of some parameters when you have other parameters like fn foo<'a>(_: &&'a Foo) and elision involving &self like fn foo(&self, _: &Foo) -> &Foo. For these, respectively, it should print something along the lines of:

note: fully inferred function definition
      fn foo<'a, 'anon0>(_: &'anon0 &'a Foo) { ... }

and

note: fully inferred function definition
      fn foo<'anon0, 'anon1>(&'anon0 self, _: &'anon1 Foo) -> &'anon0 Foo { ... }

Vurich commented Aug 23, 2017

I was referred here for a comment I made about how lifetime errors involving complex elision should print the fully elided parameters. By complex elision I mean elision that doesn't just result in the same lifetime for every param, so that's elision of some parameters when you have other parameters like fn foo<'a>(_: &&'a Foo) and elision involving &self like fn foo(&self, _: &Foo) -> &Foo. For these, respectively, it should print something along the lines of:

note: fully inferred function definition
      fn foo<'a, 'anon0>(_: &'anon0 &'a Foo) { ... }

and

note: fully inferred function definition
      fn foo<'anon0, 'anon1>(&'anon0 self, _: &'anon1 Foo) -> &'anon0 Foo { ... }
@nikomatsakis

This comment has been minimized.

Show comment
Hide comment
@nikomatsakis

nikomatsakis Sep 11, 2017

Contributor

@Vurich it'd be good to see an example of a case where you think that'd be useful. We've been trying so far to make more targeted suggestions -- see the updated game plan above -- but I could see that it might be useful to show the "fully inferred" function definition in some cases.

Contributor

nikomatsakis commented Sep 11, 2017

@Vurich it'd be good to see an example of a case where you think that'd be useful. We've been trying so far to make more targeted suggestions -- see the updated game plan above -- but I could see that it might be useful to show the "fully inferred" function definition in some cases.

@PieterPenninckx

This comment has been minimized.

Show comment
Hide comment
@PieterPenninckx

PieterPenninckx Sep 15, 2017

Contributor

@nikomatsakis I think Vurich's original example was the following (taken from his comment)

struct Bar<'a, T: 'a>(&'a mut T);
struct Foo<'a, T: 'a>(&'a mut Bar<'a, T>);

fn make_foo<'a, T>(m: &'a mut Bar<T>) -> Foo<'a, T> {
    Foo(m)
}

When I try to compile this, the error message already mentions an anonymous lifetime:

note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the function body at 4:1...
 --> test.rs:4:1
  |
4 | / fn make_foo<'a, T>(m: &'a mut Bar<T>) -> Foo<'a, T> {
5 | |     Foo(m)
6 | | }
  | |_^

I think this would be the perfect place to show the fully inferred function definition ("anonymous lifetime #1" is a little vague):

note: but, the lifetime must be valid for the anonymous lifetime 'anon1 defined on the function body at 4:1...
 --> test.rs:4:1
  |
4 | / fn make_foo<'a, T>(m: &'a mut Bar<T>) -> Foo<'a, T> {
5 | |     Foo(m)
6 | | }
  | |_^
note: fully inferred function definition of test.rs:4:1
  | fn make_foo<'a, 'anon0, T>(m: &'a mut Bar<'anon0, T>) -> Foo<'a, T>
Contributor

PieterPenninckx commented Sep 15, 2017

@nikomatsakis I think Vurich's original example was the following (taken from his comment)

struct Bar<'a, T: 'a>(&'a mut T);
struct Foo<'a, T: 'a>(&'a mut Bar<'a, T>);

fn make_foo<'a, T>(m: &'a mut Bar<T>) -> Foo<'a, T> {
    Foo(m)
}

When I try to compile this, the error message already mentions an anonymous lifetime:

note: but, the lifetime must be valid for the anonymous lifetime #1 defined on the function body at 4:1...
 --> test.rs:4:1
  |
4 | / fn make_foo<'a, T>(m: &'a mut Bar<T>) -> Foo<'a, T> {
5 | |     Foo(m)
6 | | }
  | |_^

I think this would be the perfect place to show the fully inferred function definition ("anonymous lifetime #1" is a little vague):

note: but, the lifetime must be valid for the anonymous lifetime 'anon1 defined on the function body at 4:1...
 --> test.rs:4:1
  |
4 | / fn make_foo<'a, T>(m: &'a mut Bar<T>) -> Foo<'a, T> {
5 | |     Foo(m)
6 | | }
  | |_^
note: fully inferred function definition of test.rs:4:1
  | fn make_foo<'a, 'anon0, T>(m: &'a mut Bar<'anon0, T>) -> Foo<'a, T>
@PieterPenninckx

This comment has been minimized.

Show comment
Hide comment
@PieterPenninckx

PieterPenninckx Sep 15, 2017

Contributor

I had a problem similar to @Vurich 's example when I did my first experiments with trait objects. A simplified version of the code is the following:

trait T {}

struct TT<'a> { x: &'a i8 }

impl<'a> T for TT<'a> {}

struct S { data: Vec<Box<T>> }

impl S {
    fn new() -> Self { S{data: Vec::new()} }
    fn push(&mut self, t: Box<T>) { self.data.push(t); }
}

fn boxed_trait_object() {
    let i = 4;
    let t = TT{x: &i};
    let mut s = S::new();
    s.push(Box::new(t)); // Error: `i` does not live long enough
                         // Note: borrowed value must be valid 
                         //   for the static lifetime...
}

I started searching my code for the 'static lifetime to find out how why I got this error. After some unsuccessful attempts, I switched strategy and tried to figure out what potential bug the compiler stopped me from making. Eventually I figured out that the type S should have a lifetime parameter because s borrows i and that hence the trait object should have a lifetime parameter. I did an internet search for "rust how to add a lifetime parameter to a trait object" or similar because I didn't know that the proper terminology is "lifetime bound for trait object". I added the lifetime bound to the trait object and made the compiler happy. It took me about a whole afternoon to fix this compiler error.

The only solution I can think of to make it clearer what happens in situations like this is to warn on default lifetimes for trait objects. I know that RFC rust-lang/rfcs#2115 proposes to warn on leaving off lifetime parameters on structs. Is a similar warning planned for leaving off lifetime bounds on trait objects?

Contributor

PieterPenninckx commented Sep 15, 2017

I had a problem similar to @Vurich 's example when I did my first experiments with trait objects. A simplified version of the code is the following:

trait T {}

struct TT<'a> { x: &'a i8 }

impl<'a> T for TT<'a> {}

struct S { data: Vec<Box<T>> }

impl S {
    fn new() -> Self { S{data: Vec::new()} }
    fn push(&mut self, t: Box<T>) { self.data.push(t); }
}

fn boxed_trait_object() {
    let i = 4;
    let t = TT{x: &i};
    let mut s = S::new();
    s.push(Box::new(t)); // Error: `i` does not live long enough
                         // Note: borrowed value must be valid 
                         //   for the static lifetime...
}

I started searching my code for the 'static lifetime to find out how why I got this error. After some unsuccessful attempts, I switched strategy and tried to figure out what potential bug the compiler stopped me from making. Eventually I figured out that the type S should have a lifetime parameter because s borrows i and that hence the trait object should have a lifetime parameter. I did an internet search for "rust how to add a lifetime parameter to a trait object" or similar because I didn't know that the proper terminology is "lifetime bound for trait object". I added the lifetime bound to the trait object and made the compiler happy. It took me about a whole afternoon to fix this compiler error.

The only solution I can think of to make it clearer what happens in situations like this is to warn on default lifetimes for trait objects. I know that RFC rust-lang/rfcs#2115 proposes to warn on leaving off lifetime parameters on structs. Is a similar warning planned for leaving off lifetime bounds on trait objects?

@estebank

This comment has been minimized.

Show comment
Hide comment
@estebank

estebank Sep 21, 2017

Contributor

I believe #44684 might be a duplicate of #42703. Leaving open for now until verifying that this is the case.

Contributor

estebank commented Sep 21, 2017

I believe #44684 might be a duplicate of #42703. Leaving open for now until verifying that this is the case.

@vext01

This comment has been minimized.

Show comment
Hide comment
@vext01

vext01 Nov 30, 2017

Thanks for working on this :)

I think the lifetime errors are the biggest usability hurdle with Rust.

/me subscribes.

vext01 commented Nov 30, 2017

Thanks for working on this :)

I think the lifetime errors are the biggest usability hurdle with Rust.

/me subscribes.

@estebank

This comment has been minimized.

Show comment
Hide comment
@estebank
Contributor

estebank commented Feb 10, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment