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

Variable lifetime is wrongly tracked in async fn #98077

Open
thalesfragoso opened this issue Jun 14, 2022 · 1 comment
Open

Variable lifetime is wrongly tracked in async fn #98077

thalesfragoso opened this issue Jun 14, 2022 · 1 comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug.

Comments

@thalesfragoso
Copy link

async fn foo() {
    let mut f = None;
    let value = 0;
    f = Some(async { value });
    core::mem::drop(f);
}

Playground. Error:

  |
4 |     f = Some(async { value });
  |                    --^^^^^--
  |                    | |
  |                    | borrowed value does not live long enough
  |                    value captured here by generator
5 |     core::mem::drop(f);
6 | }
  | -
  | |
  | `value` dropped here while still borrowed
  | borrow might be used here, when `f` is dropped and runs the destructor for type `Option<impl Future<Output = i32>>`
  |
  = note: values in a scope are dropped in the opposite order they are defined

f is manually moved with drop and could not be dropped at the end of the scope after value.

Some bonus weird behavior, this works:

struct Foo;

async fn foo() {
    let mut f = None;
    let value = Foo;
    f = Some(async { value });
    core::mem::drop(f);
}

But if you add #[derive(Copy, Clone)] to Foo, then it fails with the same error. However, this also does not work:

struct Foo;

impl Foo {
    fn push(&mut self) {}
}

async fn foo() {
    let mut f = None;
    let value = Foo;
    f = Some(async { value.push() });
    core::mem::drop(f);
}

So, probably not necessarily connected to Copy.

@thalesfragoso thalesfragoso added the C-bug Category: This is a bug. label Jun 14, 2022
@compiler-errors compiler-errors added the A-async-await Area: Async & Await label Jun 14, 2022
@eholk
Copy link
Contributor

eholk commented Jun 20, 2022

We discussed this in our triage meeting today (conversation starts here). There are a few more variations that work:

We can await the value and it works:

async fn foo() {
    let mut f = None;
    let value = 0;
    f = Some(async { value }.await);
    core::mem::drop(f);
}

We can also change the order that f and value are declared:

async fn foo() {
    let value = 0;
    let mut f = None;
    f = Some(async { value });
    core::mem::drop(f);
}

Using async move also works:

async fn foo() {
    let mut f = None;
    let value = 0;
    f = Some(async move { value });
    core::mem::drop(f);
}

I can explain a lot of the behavior, but the fact that drop doesn't help still confuses me.

In the cases that fail, I think f gets inferred to be something like Option<impl Future + 'a>, so f borrows something with lifetime 'a. There can be a couple reasons for the borrow. In the case of value.push(), this is sugar for Foo::push(&'a value). When value is Copy, then the use of value desugars to *&'a value, but when value is not Copy, then the use of it counts as a move and there is no borrowing.

Ignoring the call to drop, when we go out of scope we drop variables in reverse order. This means value gets dropped, and then f potentially has a dangling pointer. That's why we're getting the error. This is also why changing the declaration order fixes the bug.

The part I don't understand is why the compiler doesn't realize f is no longer live after the call to drop(f). This seems like the kind of thing it's normally able to handle. Maybe this has something to do with the fact that we're in an async fn instead of a regular fn?

@rustbot label +AsyncAwait-Triaged

@rustbot rustbot added the AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. label Jun 20, 2022
@tmandry tmandry moved this to On deck in wg-async work Dec 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-async-await Area: Async & Await AsyncAwait-Triaged Async-await issues that have been triaged during a working group meeting. C-bug Category: This is a bug.
Projects
Status: On deck
Development

No branches or pull requests

4 participants