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

Confusing error involving lifetime elision #79879

Open
ZainlessBrombie opened this issue Dec 10, 2020 · 9 comments
Open

Confusing error involving lifetime elision #79879

ZainlessBrombie opened this issue Dec 10, 2020 · 9 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@ZainlessBrombie
Copy link

The following code:

struct A<'a> {
    backref: Option<&'a mut A<'a>>,
    value: String,
}

impl<'a> A<'a> {
    fn child_with_value(&'a mut self, value: String) -> A<'a> {
        A {
            backref: Some(self),
            value,
        }
    }
}

fn works(a: &mut A) {
    println!("{}", a.value);
}

fn errors(a: &mut A) {
    let mut b = a.child_with_value("foo".into());
    errors(&mut b);
}

Gives this error:
image

I expected to see this happen:
Given that the invocation should be legal, I expect this to compile without issues :)

Instead, this happened: Compiler error above. Note that the variable flows into itself, which seems strange

Meta

Tested both on stable and nightly:
rustc --version --verbose:

rustc 1.50.0-nightly (f0f68778f 2020-12-09)
binary: rustc
commit-hash: f0f68778f798d6d34649745b41770829b17ba5b8
commit-date: 2020-12-09
host: x86_64-unknown-linux-gnu
release: 1.50.0-nightly

Backtrace not applicable I think?

@ZainlessBrombie ZainlessBrombie added the C-bug Category: This is a bug. label Dec 10, 2020
@camelid
Copy link
Member

camelid commented Dec 10, 2020

Backtrace not applicable I think?

Correct, you only need a backtrace if the compiler crashes.


I don't know enough about lifetimes to know whether this should work, but the diagnostics are definitely weird. Here's a more minimal example:

struct A<'a> {
    backref: Option<&'a mut A<'a>>,
}

fn child_with_value<'a>(this: &'a mut A<'a>) -> A<'a> {
    A {
        backref: Some(this),
    }
}

fn errors(a: &mut A) {
    let mut b = child_with_value(a);
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0623]: lifetime mismatch
  --> src/lib.rs:12:34
   |
11 | fn errors(a: &mut A) {
   |              ------
   |              |
   |              these two types are declared with different lifetimes...
12 |     let mut b = child_with_value(a);
   |                                  ^ ...but data from `a` flows into `a` here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0623`.
error: could not compile `playground`

To learn more, run the command again with --verbose.

@camelid camelid added A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions D-confusing Diagnostics: Confusing error or lint that should be reworked. D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. labels Dec 10, 2020
@camelid
Copy link
Member

camelid commented Dec 10, 2020

It seems the reason my minimal example fails to compile is that the compiler is not able to infer the lifetimes properly. This fixes my minimal example:

struct A<'a> {
    backref: Option<&'a mut A<'a>>,
}

fn child_with_value<'a>(this: &'a mut A<'a>) -> A<'a> {
    A {
        backref: Some(this),
    }
}

fn errors<'a>(a: &'a mut A<'a>) {
    let mut b = child_with_value(a);
}

(Playground)

@camelid
Copy link
Member

camelid commented Dec 10, 2020

However, if I add explicit lifetimes to your original code, the compiler produces this error:

error[E0597]: `b` does not live long enough
  --> src/lib.rs:21:12
   |
19 | fn errors<'a>(a: &'a mut A<'a>) {
   |           -- lifetime `'a` defined here
20 |     let mut b = a.child_with_value("foo".into());
21 |     errors(&mut b);
   |     -------^^^^^^-
   |     |      |
   |     |      borrowed value does not live long enough
   |     argument requires that `b` is borrowed for `'a`
22 | }
   | - `b` dropped here while still borrowed

Now that I see the error, I think your code is actually incorrect. (Correct me if I'm wrong on that!) The diagnostics still need to be fixed though.

@camelid camelid changed the title Strange lifetime compiler bug Confusing error involving lifetime elision Dec 10, 2020
@ZainlessBrombie
Copy link
Author

ZainlessBrombie commented Dec 10, 2020

Yes my apologies, my code is incorrect - the problem is the incorrect message :)
Thank you!

@mbartlett21
Copy link
Contributor

mbartlett21 commented Dec 10, 2020

@camelid

Edit: moved to #79886

@camelid
Copy link
Member

camelid commented Dec 10, 2020

@mbartlett21 That example appears to trigger a different issue. Please open a new issue so we can discuss it separately.

@BGR360
Copy link
Contributor

BGR360 commented Dec 29, 2021

This seems like it would be a good candidate to apply the language of #90179 to.

Here's what I picture:

error[E0623]: lifetime mismatch
  --> src/lib.rs:12:34
   |
11 | fn errors(a: &mut A) {
   |              ------
   |              |
   |              these two types are declared with different lifetimes...
12 |     let mut b = child_with_value(a);
   |                                  ^ ...but data from `a` flows into `a` here
   = note: each elided lifetime in input position becomes a distinct lifetime
help: explicitly declare a lifetime and assign it to both
   |
11 | fn errors<'a>(a: &'a mut A<'a>) {
   |          ++++     ++      ++++

I think the key point that wasn't addressed in that PR is that there's an elided lifetime parameter on A, but that PR only acts upon elided lifetimes on references (or so it would seem).

cc @Nilstrieb

@Noratrieb
Copy link
Member

sounds like a good idea, I'll look into that

@Noratrieb Noratrieb added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Apr 5, 2023
@estebank
Copy link
Contributor

Current output:

error: lifetime may not live long enough
  --> src/lib.rs:20:17
   |
19 | fn errors(a: &mut A) {
   |           -  - let's call the lifetime of this reference `'1`
   |           |
   |           has type `&mut A<'2>`
20 |     let mut b = a.child_with_value("foo".into());
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
error: lifetime may not live long enough
  --> src/lib.rs:12:17
   |
11 | fn errors(a: &mut A) {
   |           -  - let's call the lifetime of this reference `'1`
   |           |
   |           has type `&mut A<'2>`
12 |     let mut b = child_with_value(a);
   |                 ^^^^^^^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`

@estebank estebank added A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. labels Aug 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-lifetimes Area: Lifetimes / regions A-suggestion-diagnostics Area: Suggestions generated by the compiler applied by `cargo fix` C-bug Category: This is a bug. D-confusing Diagnostics: Confusing error or lint that should be reworked. D-incorrect Diagnostics: A diagnostic that is giving misleading or incorrect information. D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

6 participants