Rust: tracking issue for lifetime inference error work (E0495)

Created on 7 Jun 2017  路  12Comments  路  Source: rust-lang/rust

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!

  • [x] "explicit lifetime required" (example)

    • these are cases where the user has an explicit lifetime name, but they need to use it in more places

    • [ ] https://github.com/rust-lang/rust/issues/42700 -- we don't handle the case where the anonymous lifetime appears in self very well

  • [x] https://github.com/rust-lang/rust/issues/42703 -- "this parameter and the return type are declared with different lifetimes..." (https://github.com/rust-lang/rust/pull/44124)
  • [x] "these two types are declared with different lifetimes..." (example)

    • This is a sort of fallback. It covers all kinds of cases.

    • [x] https://github.com/rust-lang/rust/issues/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 (https://github.com/rust-lang/rust/issues/44506)


      • [ ] Suggest adding a where clause when there are already two named lifetimes (https://github.com/rust-lang/rust/issues/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:





  • Cross-cutting concerns:
  • [x] Higher-ranked closure error messages should avoid "anonymous lifetime #2 defined on the body" #45983
  • [x] smarter blame assignment for lifetime inference errors #45979

Examples in need of analysis

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).

A-diagnostics C-tracking-issue P-medium T-compiler WG-compiler-errors metabug

Most helpful comment

Thanks for working on this :)

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

/me subscribes.

All 12 comments

@nikomatsakis

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

43288

43433

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 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.

@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>

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?

I believe https://github.com/rust-lang/rust/issues/44684 might be a duplicate of https://github.com/rust-lang/rust/issues/42703. Leaving open for now until verifying that this is the case.

Thanks for working on this :)

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

/me subscribes.

CC #18759, #30257, #37879

It would be good to try to provide a catalogue/summary of the different cases here, and perhaps check how each one changes, if at all, when you switch on "full" NLL mode (as opposed to the NLL migrate mode that is the current default in nightly for all editions).

(One switches on "full" NLL via #![feature(nll)] today, or alternatively via -Z borrowck=mir if you want to do it via RUSTFLAGS)

triage: realistically we are not (and cannot) prioritize this effort (beyond what we have already done that is). Downgrading from P-high to P-medium.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

withoutboats picture withoutboats  路  308Comments

nikomatsakis picture nikomatsakis  路  331Comments

withoutboats picture withoutboats  路  202Comments

alexcrichton picture alexcrichton  路  240Comments

Leo1003 picture Leo1003  路  898Comments