Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions compiler/rustc_middle/src/mir/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -872,20 +872,25 @@ pub enum TerminatorKind<'tcx> {
/// Marks a suspend point.
///
/// Like `Return` terminators in coroutine bodies, this computes `value` and then a
/// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
/// the return place of the function calling this one, and execution continues in the calling
/// function. When next invoked with the same first argument, execution of this function
/// continues at the `resume` basic block, with the second argument written to the `resume_arg`
/// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
/// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned
/// to the return place provided by the caller function, and execution continues in this caller
/// function.
///
/// When the coroutine is resumed/polled, execution of this function continues at the `resume`
/// basic block, the `resume_arg` place is evaluated and the second argument to `resume/poll`
/// is written to it.
///
/// If the coroutine is dropped before then, execution of this function continues at the `drop`
/// basic block and the `resume_arg` place expression is evaluated. For async drop, the second
/// argument to the destructor `resume/poll` method is written to `resume_arg`. For synchronous
/// drops, uninitialized bytes are written to `resume_arg`.
///
/// Note that coroutines can be (unstably) cloned under certain conditions, which means that
/// this terminator can **return multiple times**! MIR optimizations that reorder code into
/// different basic blocks needs to be aware of that.
/// See <https://github.com/rust-lang/rust/issues/95360>.
///
/// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
///
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
Yield {
/// The value to return.
value: Operand<'tcx>,
Expand Down
32 changes: 13 additions & 19 deletions compiler/rustc_middle/src/mir/terminator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ impl<'tcx> TerminatorKind<'tcx> {
}
}

#[derive(Copy, Clone, Debug)]
#[derive(Debug)]
pub enum TerminatorEdges<'mir, 'tcx> {
/// For terminators that have no successor, like `return`.
None,
Expand All @@ -686,7 +686,7 @@ pub enum TerminatorEdges<'mir, 'tcx> {
Double(BasicBlock, BasicBlock),
/// Special action for `Yield`, `Call` and `InlineAsm` terminators.
AssignOnReturn {
return_: &'mir [BasicBlock],
return_: Box<[BasicBlock]>,
/// The cleanup block, if it exists.
cleanup: Option<BasicBlock>,
place: CallReturnPlaces<'mir, 'tcx>,
Expand Down Expand Up @@ -755,27 +755,21 @@ impl<'tcx> TerminatorKind<'tcx> {
TerminatorEdges::Double(real_target, imaginary_target)
}

Yield { resume: ref target, drop, resume_arg, value: _ } => {
Yield { resume: target, drop, resume_arg, value: _ } => {
TerminatorEdges::AssignOnReturn {
return_: slice::from_ref(target),
cleanup: drop,
return_: [target].into_iter().chain(drop.into_iter()).collect(),
cleanup: None,
place: CallReturnPlaces::Yield(resume_arg),
}
}

Call {
unwind,
destination,
ref target,
func: _,
args: _,
fn_span: _,
call_source: _,
} => TerminatorEdges::AssignOnReturn {
return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::Call(destination),
},
Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
TerminatorEdges::AssignOnReturn {
return_: target.into_iter().collect(),
cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::Call(destination),
}
}

InlineAsm {
asm_macro: _,
Expand All @@ -786,7 +780,7 @@ impl<'tcx> TerminatorKind<'tcx> {
ref targets,
unwind,
} => TerminatorEdges::AssignOnReturn {
return_: targets,
return_: targets.to_owned(),
cleanup: unwind.cleanup_block(),
place: CallReturnPlaces::InlineAsm(operands),
},
Expand Down
8 changes: 5 additions & 3 deletions compiler/rustc_mir_dataflow/src/framework/direction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,13 @@ impl Direction for Backward {
propagate(pred, &tmp);
}

mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => {
mir::TerminatorKind::Yield { resume, drop, resume_arg, .. }
if resume == block || drop == Some(block) =>
{
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
resume,
block,
CallReturnPlaces::Yield(resume_arg),
);
propagate(pred, &tmp);
Expand Down Expand Up @@ -275,7 +277,7 @@ impl Direction for Forward {

if !return_.is_empty() {
analysis.apply_call_return_effect(exit_state, block, place);
for &target in return_ {
for target in return_ {
propagate(target, exit_state);
}
}
Expand Down
18 changes: 14 additions & 4 deletions compiler/rustc_mir_transform/src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1302,13 +1302,21 @@ fn create_coroutine_resume_function<'tcx>(
enum Operation {
Resume,
Drop,
AsyncDrop,
}

impl Operation {
fn target_block(self, point: &SuspensionPoint<'_>) -> Option<BasicBlock> {
match self {
Operation::Resume => Some(point.resume),
Operation::Drop => point.drop,
Operation::Drop | Operation::AsyncDrop => point.drop,
}
}

fn resume_place<'tcx>(self, point: &SuspensionPoint<'tcx>) -> Option<Place<'tcx>> {
match self {
Operation::Resume | Operation::AsyncDrop => Some(point.resume_arg),
Operation::Drop => None,
}
}
}
Expand Down Expand Up @@ -1339,12 +1347,14 @@ fn create_cases<'tcx>(
}
}

if operation == Operation::Resume && point.resume_arg != CTX_ARG.into() {
// Move the resume argument to the destination place of the `Yield` terminator
// Move the resume argument to the destination place of the `Yield` terminator
if let Some(resume_arg) = operation.resume_place(point)
&& resume_arg != CTX_ARG.into()
{
statements.push(Statement::new(
source_info,
StatementKind::Assign(Box::new((
point.resume_arg,
resume_arg,
Rvalue::Use(Operand::Move(CTX_ARG.into()), WithRetag::Yes),
))),
));
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/coroutine/drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(

let source_info = SourceInfo::outermost(body.span);

let mut cases = create_cases(&mut body, transform, Operation::Drop);
let mut cases = create_cases(&mut body, transform, Operation::AsyncDrop);

cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a<T>()}>, _2: &mut Context<'_>)
}

bb12: {
_9 = move _2;
goto -> bb4;
}

bb13: {
_13 = move _2;
goto -> bb4;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,12 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a<T>()}>, _2: &mut Context<'_>)
}

bb16: {
_9 = move _2;
goto -> bb7;
}

bb17: {
_13 = move _2;
goto -> bb7;
}

Expand Down
Loading