Skip to content

x = yield; makes generators larger than they need to be #69672

Closed
@jonas-schievink

Description

@jonas-schievink
Contributor

This generator has a size of 4 Bytes:

    let mut gen = |mut x| {
        loop {
            drop(x);
            yield;
            x = makeit();
            useit(&x);
        }
    };

This one is 16 Bytes in size, even though it also does not need to keep x alive across the yield:

    let mut gen = |mut x| {
        loop {
            drop(x);
            x = yield;
            useit(&x);
        }
    };

(where makeit is a fn returning a usize, and useit is a fn taking &usize)

This seems to be fallout from #69302. Either the layout calculation soundness fix exposed it, or the visitor changes in there caused it.

This means that #69033 will increase the size of futures created from async fns, unless this bug is fixed.

Activity

added
T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
C-bugCategory: This is a bug.
I-heavyIssue: Problems and improvements with respect to binary size of generated code.
F-coroutines`#![feature(coroutines)]`
on Mar 3, 2020
hanna-kruppe

hanna-kruppe commented on Mar 3, 2020

@hanna-kruppe
Contributor

FWIW the evaluation order of <place> = <expr> (at MIR level) is currently: first evaluate <expr> (into a temporary), then drop <place>. For types without drop glue (like usize) that shouldn't matter, and even for types with drop glue there's reasons to change that if we can (to be able to avoid temporaries). But still, the evaluation order being this way currently means x would, in fact, have to be kept alive across the yield if it had drop glue or might have drop glue (since MIR is polymorphic, not monomorphized, and the generator->state machine transform happens on MIR).

jonas-schievink

jonas-schievink commented on Mar 3, 2020

@jonas-schievink
ContributorAuthor

True, but shouldn't the unconditional drop(x); before the yield prevent that? The value wouldn't be live anymore when the yield happens.

(there's also a separate bug where a type being Copy can actually make a generator larger, but that also shouldn't happen here)

hanna-kruppe

hanna-kruppe commented on Mar 3, 2020

@hanna-kruppe
Contributor

Oh, I missed that somehow. That liveness is not taken into account for generator captures is a problem independent of resume arguments, see #69663. Could async fns after #69033 have such an explicit drop too or would they still be affected if #69663 was fixed?

added
AsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.
on Mar 3, 2020
jonas-schievink

jonas-schievink commented on Mar 3, 2020

@jonas-schievink
ContributorAuthor

#69663 is about typeck's view of captures from what I can tell. The transform itself (which determines the memory layout) runs on optimized MIR and should already be able to tell whether variables are really live across yield points.

tmandry

tmandry commented on Mar 3, 2020

@tmandry
Member

@rustbot claim

Assigning myself to think about this issue and leave notes on the underlying issues involved.

5 remaining items

Loading
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-coroutinesArea: CoroutinesAsyncAwait-TriagedAsync-await issues that have been triaged during a working group meeting.C-bugCategory: This is a bug.F-coroutines`#![feature(coroutines)]`I-heavyIssue: Problems and improvements with respect to binary size of generated code.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

    Participants

    @jonas-schievink@tmandry@hanna-kruppe

    Issue actions

      `x = yield;` makes generators larger than they need to be · Issue #69672 · rust-lang/rust