Skip to content

Unused arguments to async fn are dropped too early #54716

Closed
@Nemo157

Description

@Nemo157
Member

Unused arguments to async fn are not moved into the resulting generator so are dropped before the future runs, here's some example psuedo-code demonstrating this (full running playground example here):

async fn foo(log1: DropLog, _log2: DropLog) {
    println!("Got log1: {:?}", log1);
}

fn bar(log1: DropLog, _log2: DropLog) {
    println!("Got log1: {:?}", log1);
}

fn main() {
    foo(DropLog(1), DropLog(2)).poll(...);
    println!();
    bar(DropLog(3), DropLog(4));
}

which gives the output:

Dropped DropLog(2)
Got log1: DropLog(1)
Dropped DropLog(1)

Got log1: DropLog(3)
Dropped DropLog(4)
Dropped DropLog(3)

I found this because of a related issue with futures-await(0.1) posted to urlo, it's at the very least surprising behaviour that needs documenting if not a bug.

Activity

added
A-destructorsArea: Destructors (`Drop`, …)
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
C-bugCategory: This is a bug.
on Jan 27, 2019
nikomatsakis

nikomatsakis commented on Mar 5, 2019

@nikomatsakis
Contributor

Marking this as blocking stabilization of async-await. Obviously, changing the semantics here would alter the semantics of stable code. Moreover, there's a good case to be made that the current semantics are incorrect. @cramertj and I (along with a few others) discussed this in a recent meeting. Our conclusion was that we effectively wanted a desugaring like the following:

async fn foo(<pattern> @ x: Type) {
}

// becomes
fn foo(<pattern> @ x: Type) {
  async move {
    let <pattern> = x;
  } // <-- as you "exit" the async block
}

where x is a kind of "fresh name" not visible to the user.

Some of the key test cases we want to consider would be:

  • async fn foo(_: Foo) -- the Foo value should be dropped after the future is "waited"
  • async fn foo(_x: Foo) -- same

This is quite analogous then to the behavior for fn foo(_: Foo) and friends -- basically after the "return" executes, the parameters are dropped (whereas current behavior drops before the body of the function even begins (i.e., before the future is polled)).

nikomatsakis

nikomatsakis commented on Mar 5, 2019

@nikomatsakis
Contributor

Tagging as E-needs-mentor as we need to write up some notes on how to alter the desugaring here. One bit I can see being complex is that the HIR presently has no way to "name" a parameter apart from entering a pattern.

self-assigned this
on Mar 5, 2019
removed their assignment
on Apr 2, 2019
cramertj

cramertj commented on Apr 2, 2019

@cramertj
Member

(nominating for discussion in T-lang to resolve the desired behavior)

cramertj

cramertj commented on Apr 2, 2019

@cramertj
Member

Summary of the current issues by @davidtwco can be found on Zulip.

17 remaining items

najamelan

najamelan commented on Apr 19, 2019

@najamelan
Contributor

@cramertj Sorry if this is a noob question, but is the drop order for non-async functions: _ => drop early, _ident => drop late? And will that be consistent behaviour everywhere, with the user being able to opt into perf gain by using just _?

cramertj

cramertj commented on Apr 19, 2019

@cramertj
Member

And will that be consistent behaviour everywhere, with the user being able to opt into perf gain by using just _?

No, the performance gain being discussed here is not achievable by using _ in argument position. _ bindings in argument position drop at the end of the function, not at the start (or, in this case, prior to the future even having started).

CAD97

CAD97 commented on Apr 19, 2019

@CAD97
Contributor

Would that be a viable future change (for sync and async) to put through Crater? (As iirc parameter drop order isn't actually specified and it would make it more consistent with let.)

It doesn't need to be, but it seems desirable on the surface.

cramertj

cramertj commented on Apr 19, 2019

@cramertj
Member

@CAD97 That would be a breaking change to a pretty significant amount of existing code, a significant chunk of which is probably unsafe (e.g. functions using _: Mutex<()> as a guard, as I mentioned above).

comex

comex commented on Apr 20, 2019

@comex
Contributor

OTOH, if the behavior were changed in an edition, it would be a relatively straightforward automatic migration...

self-assigned this
on Apr 21, 2019
davidtwco

davidtwco commented on Apr 21, 2019

@davidtwco
Member

Assigning myself again since I have an open PR for this.

added a commit that references this issue on Apr 23, 2019

Rollup merge of rust-lang#59823 - davidtwco:issue-54716, r=cramertj

fbc5e88
added 2 commits that reference this issue on Apr 23, 2019

Rollup merge of rust-lang#59823 - davidtwco:issue-54716, r=cramertj

62d1574

Rollup merge of rust-lang#59823 - davidtwco:issue-54716, r=cramertj

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

Metadata

Metadata

Assignees

Labels

A-async-awaitArea: Async & AwaitA-destructorsArea: Destructors (`Drop`, …)AsyncAwait-PolishAsync-await issues that are part of the "polish" areaC-bugCategory: This is a bug.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-langRelevant to the language team

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    Participants

    @comex@Nemo157@nikomatsakis@pnkfelix@najamelan

    Issue actions

      Unused arguments to async fn are dropped too early · Issue #54716 · rust-lang/rust