Skip to main content

Module coroutine

Module coroutine 

Source
Expand description

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

MIR generation for coroutines creates a function which has a self argument which passes by value. This argument is effectively a coroutine type which only contains upvars and is only used for this argument inside the MIR for the coroutine. 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 coroutine argument (which only consists of upvars) if any of the upvars are moved out of. This pass elaborates the drops of upvars / coroutine argument in the case that none of the upvars were moved out of. This is because we cannot have any drops of this coroutine in the MIR, since it is used to create the drop glue for the coroutine. Weโ€™d get infinite recursion otherwise.

This pass creates the implementation for either the Coroutine::resume or Future::poll function and the drop shim for the coroutine based on the MIR input. It converts the coroutine argument from Self to &mut Self adding derefs in the MIR as needed. It computes the final layout of the coroutine struct which looks like this: First upvars are stored It is followed by the coroutine state field. Then finally the MIR locals which are live across a suspension point are stored. ignore (illustrative) struct Coroutine { 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 coroutine states: 0 - Coroutine have not been resumed yet 1 - Coroutine has returned / is completed 2 - Coroutine has been poisoned

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

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

One of them is the implementation of Coroutine::resume / Future::poll. For coroutines with state 0 (unresumed) it starts the execution of the coroutine. For coroutines 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 coroutine. For coroutines with state 0 (unresumed) it drops the upvars of the coroutine. For coroutines with state 1 (returned) and state 2 (poisoned) it does nothing. Otherwise it drops all the values in scope at the last suspension point.

Modulesยง

by_move_body ๐Ÿ”’
This pass constructs a second coroutine body sufficient for return from FnOnce/AsyncFnOnce implementations for coroutine-closures (e.g. async closures).
drop ๐Ÿ”’
Drops and async drops related logic for coroutine transformation pass
layout ๐Ÿ”’
Coroutine StateTransform inverts control flow in a coroutine from a function with yield points to a state machine. Each yield point corresponds to a state variant, and each variant stores the locals that are needed to continue the coroutine.

Structsยง

EnsureCoroutineFieldAssignmentsNeverAlias ๐Ÿ”’
Looks for any assignments between locals (e.g., _4 = _5) that will both be converted to fields in the coroutine state machine but whose storage is not marked as conflicting
RenameLocalVisitor ๐Ÿ”’
SelfArgVisitor ๐Ÿ”’
StateTransform ๐Ÿ”’
SuspensionPoint ๐Ÿ”’
A yield point in the coroutine.
TransformVisitor ๐Ÿ”’

Enumsยง

Operation ๐Ÿ”’
An operation that can be performed on a coroutine.

Constantsยง

CTX_ARG ๐Ÿ”’
SELF_ARG ๐Ÿ”’

Functionsยง

can_return ๐Ÿ”’
can_unwind ๐Ÿ”’
create_cases ๐Ÿ”’
create_coroutine_resume_function ๐Ÿ”’
eliminate_get_context_calls ๐Ÿ”’
HIR uses get_context to unwrap a &mut Context<'_> from a ResumeTy. Both types are just a single pointer, but liveness analysis does not know that and supposes that the operand and the destination are live at the same time. Forcibly inline those calls to avoid this.
generate_poison_block_and_redirect_unwinds_there ๐Ÿ”’
insert_panic_block ๐Ÿ”’
insert_poll_ready_block ๐Ÿ”’
insert_switch ๐Ÿ”’
Replaces the entry point of body with a block that switches on the coroutine discriminant and dispatches to blocks according to cases.
insert_term_block ๐Ÿ”’
make_aggregate_adt ๐Ÿ”’
make_coroutine_state_argument_indirect ๐Ÿ”’
make_coroutine_state_argument_pinned ๐Ÿ”’
replace_base ๐Ÿ”’
return_poll_ready_assign ๐Ÿ”’
transform_async_context ๐Ÿ”’
Async desugaring uses an unsafe binder type ResumeTy to circumvert borrow-checking. The ResumeTy hides a &mut Context<'_> behind an unsafe raw pointer, and the get_context function is being used to convert that back to a &mut Context<'_>.