Skip to content

Commit bb4f119

Browse files
authored
Unrolled build for #156875
Rollup merge of #156875 - cjgillot:yield-drop-resume, r=oli-obk Correct and document semantics of `yield` terminator The current implementation of dataflow for the yield terminator confuses the `drop` target with unwinding. Which is wrong, in particular when implementing async drops. r? @RalfJung
2 parents c58275e + b542677 commit bb4f119

7 files changed

Lines changed: 49 additions & 34 deletions

File tree

compiler/rustc_middle/src/mir/syntax.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -872,20 +872,25 @@ pub enum TerminatorKind<'tcx> {
872872
/// Marks a suspend point.
873873
///
874874
/// Like `Return` terminators in coroutine bodies, this computes `value` and then a
875-
/// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned to
876-
/// the return place of the function calling this one, and execution continues in the calling
877-
/// function. When next invoked with the same first argument, execution of this function
878-
/// continues at the `resume` basic block, with the second argument written to the `resume_arg`
879-
/// place. If the coroutine is dropped before then, the `drop` basic block is invoked.
875+
/// `CoroutineState::Yielded(value)` as if by `Aggregate` rvalue. That value is then assigned
876+
/// to the return place provided by the caller function, and execution continues in this caller
877+
/// function.
878+
///
879+
/// When the coroutine is resumed/polled, execution of this function continues at the `resume`
880+
/// basic block, the `resume_arg` place is evaluated and the second argument to `resume/poll`
881+
/// is written to it.
882+
///
883+
/// If the coroutine is dropped before then, execution of this function continues at the `drop`
884+
/// basic block and the `resume_arg` place expression is evaluated. For async drop, the second
885+
/// argument to the destructor `resume/poll` method is written to `resume_arg`. For synchronous
886+
/// drops, uninitialized bytes are written to `resume_arg`.
880887
///
881888
/// Note that coroutines can be (unstably) cloned under certain conditions, which means that
882889
/// this terminator can **return multiple times**! MIR optimizations that reorder code into
883890
/// different basic blocks needs to be aware of that.
884891
/// See <https://github.com/rust-lang/rust/issues/95360>.
885892
///
886893
/// Not permitted in bodies that are not coroutine bodies, or after coroutine lowering.
887-
///
888-
/// **Needs clarification**: What about the evaluation order of the `resume_arg` and `value`?
889894
Yield {
890895
/// The value to return.
891896
value: Operand<'tcx>,

compiler/rustc_middle/src/mir/terminator.rs

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ impl<'tcx> TerminatorKind<'tcx> {
674674
}
675675
}
676676

677-
#[derive(Copy, Clone, Debug)]
677+
#[derive(Debug)]
678678
pub enum TerminatorEdges<'mir, 'tcx> {
679679
/// For terminators that have no successor, like `return`.
680680
None,
@@ -686,7 +686,7 @@ pub enum TerminatorEdges<'mir, 'tcx> {
686686
Double(BasicBlock, BasicBlock),
687687
/// Special action for `Yield`, `Call` and `InlineAsm` terminators.
688688
AssignOnReturn {
689-
return_: &'mir [BasicBlock],
689+
return_: Box<[BasicBlock]>,
690690
/// The cleanup block, if it exists.
691691
cleanup: Option<BasicBlock>,
692692
place: CallReturnPlaces<'mir, 'tcx>,
@@ -755,27 +755,21 @@ impl<'tcx> TerminatorKind<'tcx> {
755755
TerminatorEdges::Double(real_target, imaginary_target)
756756
}
757757

758-
Yield { resume: ref target, drop, resume_arg, value: _ } => {
758+
Yield { resume: target, drop, resume_arg, value: _ } => {
759759
TerminatorEdges::AssignOnReturn {
760-
return_: slice::from_ref(target),
761-
cleanup: drop,
760+
return_: [target].into_iter().chain(drop.into_iter()).collect(),
761+
cleanup: None,
762762
place: CallReturnPlaces::Yield(resume_arg),
763763
}
764764
}
765765

766-
Call {
767-
unwind,
768-
destination,
769-
ref target,
770-
func: _,
771-
args: _,
772-
fn_span: _,
773-
call_source: _,
774-
} => TerminatorEdges::AssignOnReturn {
775-
return_: target.as_ref().map(slice::from_ref).unwrap_or_default(),
776-
cleanup: unwind.cleanup_block(),
777-
place: CallReturnPlaces::Call(destination),
778-
},
766+
Call { unwind, destination, target, func: _, args: _, fn_span: _, call_source: _ } => {
767+
TerminatorEdges::AssignOnReturn {
768+
return_: target.into_iter().collect(),
769+
cleanup: unwind.cleanup_block(),
770+
place: CallReturnPlaces::Call(destination),
771+
}
772+
}
779773

780774
InlineAsm {
781775
asm_macro: _,
@@ -786,7 +780,7 @@ impl<'tcx> TerminatorKind<'tcx> {
786780
ref targets,
787781
unwind,
788782
} => TerminatorEdges::AssignOnReturn {
789-
return_: targets,
783+
return_: targets.to_owned(),
790784
cleanup: unwind.cleanup_block(),
791785
place: CallReturnPlaces::InlineAsm(operands),
792786
},

compiler/rustc_mir_dataflow/src/framework/direction.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,13 @@ impl Direction for Backward {
101101
propagate(pred, &tmp);
102102
}
103103

104-
mir::TerminatorKind::Yield { resume, resume_arg, .. } if resume == block => {
104+
mir::TerminatorKind::Yield { resume, drop, resume_arg, .. }
105+
if resume == block || drop == Some(block) =>
106+
{
105107
let mut tmp = exit_state.clone();
106108
analysis.apply_call_return_effect(
107109
&mut tmp,
108-
resume,
110+
block,
109111
CallReturnPlaces::Yield(resume_arg),
110112
);
111113
propagate(pred, &tmp);
@@ -275,7 +277,7 @@ impl Direction for Forward {
275277

276278
if !return_.is_empty() {
277279
analysis.apply_call_return_effect(exit_state, block, place);
278-
for &target in return_ {
280+
for target in return_ {
279281
propagate(target, exit_state);
280282
}
281283
}

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,13 +1302,21 @@ fn create_coroutine_resume_function<'tcx>(
13021302
enum Operation {
13031303
Resume,
13041304
Drop,
1305+
AsyncDrop,
13051306
}
13061307

13071308
impl Operation {
13081309
fn target_block(self, point: &SuspensionPoint<'_>) -> Option<BasicBlock> {
13091310
match self {
13101311
Operation::Resume => Some(point.resume),
1311-
Operation::Drop => point.drop,
1312+
Operation::Drop | Operation::AsyncDrop => point.drop,
1313+
}
1314+
}
1315+
1316+
fn resume_place<'tcx>(self, point: &SuspensionPoint<'tcx>) -> Option<Place<'tcx>> {
1317+
match self {
1318+
Operation::Resume | Operation::AsyncDrop => Some(point.resume_arg),
1319+
Operation::Drop => None,
13121320
}
13131321
}
13141322
}
@@ -1339,12 +1347,14 @@ fn create_cases<'tcx>(
13391347
}
13401348
}
13411349

1342-
if operation == Operation::Resume && point.resume_arg != CTX_ARG.into() {
1343-
// Move the resume argument to the destination place of the `Yield` terminator
1350+
// Move the resume argument to the destination place of the `Yield` terminator
1351+
if let Some(resume_arg) = operation.resume_place(point)
1352+
&& resume_arg != CTX_ARG.into()
1353+
{
13441354
statements.push(Statement::new(
13451355
source_info,
13461356
StatementKind::Assign(Box::new((
1347-
point.resume_arg,
1357+
resume_arg,
13481358
Rvalue::Use(Operand::Move(CTX_ARG.into()), WithRetag::Yes),
13491359
))),
13501360
));

compiler/rustc_mir_transform/src/coroutine/drop.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,7 @@ pub(super) fn create_coroutine_drop_shim_async<'tcx>(
646646

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

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

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

tests/mir-opt/coroutine/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-abort.mir

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,12 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a<T>()}>, _2: &mut Context<'_>)
8686
}
8787

8888
bb12: {
89+
_9 = move _2;
8990
goto -> bb4;
9091
}
9192

9293
bb13: {
94+
_13 = move _2;
9395
goto -> bb4;
9496
}
9597

tests/mir-opt/coroutine/async_drop_live_dead.a-{closure#0}.coroutine_drop_async.0.panic-unwind.mir

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,12 @@ fn a::{closure#0}(_1: Pin<&mut {async fn body of a<T>()}>, _2: &mut Context<'_>)
105105
}
106106

107107
bb16: {
108+
_9 = move _2;
108109
goto -> bb7;
109110
}
110111

111112
bb17: {
113+
_13 = move _2;
112114
goto -> bb7;
113115
}
114116

0 commit comments

Comments
 (0)