Expand description

This is the implementation of the pass which transforms generators into state machines.

MIR generation for generators creates a function which has a self argument which passes by value. This argument is effectively a generator type which only contains upvars and is only used for this argument inside the MIR for the generator. It is passed by value to enable upvars to be moved out of it. Drop elaboration runs on that MIR before this pass and creates drop flags for MIR locals. It will also drop the generator argument (which only consists of upvars) if any of the upvars are moved out of. This pass elaborates the drops of upvars / generator argument in the case that none of the upvars were moved out of. This is because we cannot have any drops of this generator in the MIR, since it is used to create the drop glue for the generator. We’d get infinite recursion otherwise.

This pass creates the implementation for the Generator::resume function and the drop shim for the generator based on the MIR input. It converts the generator argument from Self to &mut Self adding derefs in the MIR as needed. It computes the final layout of the generator struct which looks like this: First upvars are stored It is followed by the generator state field. Then finally the MIR locals which are live across a suspension point are stored. ignore (illustrative) struct Generator { upvars..., state: u32, mir_locals..., } This pass computes the meaning of the state field and the MIR locals which are live across a suspension point. There are however three hardcoded generator states: 0 - Generator have not been resumed yet 1 - Generator has returned / is completed 2 - Generator has been poisoned

It also rewrites return x and yield y as setting a new generator state and returning GeneratorState::Complete(x) and GeneratorState::Yielded(y) respectively. MIR locals which are live across a suspension point are moved to the generator struct with references to them being updated with references to the generator struct.

The pass creates two functions which have a switch on the generator state giving the action to take.

One of them is the implementation of Generator::resume. For generators with state 0 (unresumed) it starts the execution of the generator. For generators with state 1 (returned) and state 2 (poisoned) it panics. Otherwise it continues the execution from the last suspension point.

The other function is the drop glue for the generator. For generators with state 0 (unresumed) it drops the upvars of the generator. For generators with state 1 (returned) and state 2 (poisoned) it does nothing. Otherwise it drops all the values in scope at the last suspension point.

Structs

Looks for any assignments between locals (e.g., _4 = _5) that will both be converted to fields in the generator state machine but whose storage is not marked as conflicting
The set of Locals that must be saved across yield points.
A yield point in the generator.

Enums

Operation 🔒
An operation that can be performed on a generator.

Constants

POISONED 🔒
Generator has panicked and is poisoned.
Number of variants to reserve in generator state. Corresponds to UNRESUMED (beginning of a generator) and RETURNED/POISONED (end of a generator) states.
RETURNED 🔒
Generator has returned / is completed.
SELF_ARG 🔒
UNRESUMED 🔒
Generator has not been resumed yet.

Functions

can_return 🔒
can_unwind 🔒
For every saved local, looks for which locals are StorageLive at the same time. Generates a bitset for every local of all the other locals that may be StorageLive simultaneously with that local. This is used in the layout computation; see GeneratorLayout for more.
Replaces the entry point of body with a block that switches on the generator discriminant and dispatches to blocks according to cases.
Allocates a new local and replaces all references of local with it. Returns the new local.
Validates the typeck view of the generator against the actual set of types saved between yield points.