Properly handle drops for tail calls

This commit is contained in:
DrMeepster 2023-06-14 01:12:25 -07:00 committed by Maybe Lapkin
parent 484152d562
commit 4187cdc013
12 changed files with 1443 additions and 18 deletions

View File

@ -95,7 +95,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
ExprKind::Become { value } => {
let v = &this.thir[value];
let ExprKind::Scope { value, .. } = v.kind else {
let ExprKind::Scope { value, lint_level, region_scope } = v.kind else {
span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
};
@ -104,27 +104,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
};
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Vec<_> = args
.into_iter()
.copied()
.map(|arg| Spanned {
node: unpack!(block = this.as_local_call_operand(block, arg)),
span: this.thir.exprs[arg].span,
})
.collect();
this.in_scope((region_scope, source_info), lint_level, |this| {
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Vec<_> = args
.into_iter()
.copied()
.map(|arg| Spanned {
node: unpack!(block = this.as_local_call_operand(block, arg)),
span: this.thir.exprs[arg].span,
})
.collect();
this.record_operands_moved(&args);
this.record_operands_moved(&args);
debug!("expr_into_dest: fn_span={:?}", fn_span);
debug!("expr_into_dest: fn_span={:?}", fn_span);
this.cfg.terminate(
block,
source_info,
TerminatorKind::TailCall { func: fun, args, fn_span },
);
unpack!(block = this.break_for_tail_call(block, &args, source_info));
this.cfg.start_new_block().unit()
this.cfg.terminate(
block,
source_info,
TerminatorKind::TailCall { func: fun, args, fn_span },
);
this.cfg.start_new_block().unit()
})
}
_ => {
assert!(

View File

@ -745,6 +745,91 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume);
}
/// Sets up the drops for explict tail calls.
///
/// Unlike other kinds of early exits, tail calls do not go through the drop tree.
/// Instead, all scheduled drops are immediately added to the CFG.
pub(crate) fn break_for_tail_call(
&mut self,
mut block: BasicBlock,
args: &[Spanned<Operand<'tcx>>],
source_info: SourceInfo,
) -> BlockAnd<()> {
let arg_drops: Vec<_> = args
.iter()
.rev()
.filter_map(|arg| match &arg.node {
Operand::Copy(_) => bug!("copy op in tail call args"),
Operand::Move(place) => {
let local =
place.as_local().unwrap_or_else(|| bug!("projection in tail call args"));
Some(DropData { source_info, local, kind: DropKind::Value })
}
Operand::Constant(_) => None,
})
.collect();
let mut unwind_to = self.diverge_cleanup_target(
self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope,
DUMMY_SP,
);
let unwind_drops = &mut self.scopes.unwind_drops;
// the innermost scope contains only the destructors for the tail call arguments
// we only want to drop these in case of a panic, so we skip it
for scope in self.scopes.scopes[1..].iter().rev().skip(1) {
// FIXME(explicit_tail_calls) code duplication with `build_scope_drops`
for drop_data in scope.drops.iter().rev() {
let source_info = drop_data.source_info;
let local = drop_data.local;
match drop_data.kind {
DropKind::Value => {
// `unwind_to` should drop the value that we're about to
// schedule. If dropping this value panics, then we continue
// with the *next* value on the unwind path.
debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local);
debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
unwind_to = unwind_drops.drops[unwind_to].next;
let mut unwind_entry_point = unwind_to;
// the tail call arguments must be dropped if any of these drops panic
for drop in arg_drops.iter().copied() {
unwind_entry_point = unwind_drops.add_drop(drop, unwind_entry_point);
}
unwind_drops.add_entry_point(block, unwind_entry_point);
let next = self.cfg.start_new_block();
self.cfg.terminate(
block,
source_info,
TerminatorKind::Drop {
place: local.into(),
target: next,
unwind: UnwindAction::Continue,
replace: false,
},
);
block = next;
}
DropKind::Storage => {
// Only temps and vars need their storage dead.
assert!(local.index() > self.arg_count);
self.cfg.push(
block,
Statement { source_info, kind: StatementKind::StorageDead(local) },
);
}
}
}
}
block.unit()
}
fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
// If we are emitting a `drop` statement, we need to have the cached
// diverge cleanup pads ready in case that drop panics.

View File

@ -0,0 +1,108 @@
- // MIR for `f` before ElaborateDrops
+ // MIR for `f` after ElaborateDrops
fn f() -> () {
let mut _0: ();
let mut _1: !;
let _2: std::string::String;
let _6: ();
let mut _7: std::string::String;
+ let mut _8: bool;
scope 1 {
debug _a => _2;
let _3: i32;
scope 2 {
debug _b => _3;
let _4: std::string::String;
scope 3 {
debug _c => _4;
let _5: std::string::String;
scope 4 {
debug _d => _5;
}
}
}
}
bb0: {
+ _8 = const false;
StorageLive(_2);
_2 = String::new() -> [return: bb1, unwind: bb12];
}
bb1: {
StorageLive(_3);
_3 = const 12_i32;
StorageLive(_4);
_4 = String::new() -> [return: bb2, unwind: bb11];
}
bb2: {
+ _8 = const true;
StorageLive(_5);
_5 = String::new() -> [return: bb3, unwind: bb10];
}
bb3: {
StorageLive(_6);
StorageLive(_7);
+ _8 = const false;
_7 = move _4;
_6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb8];
}
bb4: {
StorageDead(_7);
StorageDead(_6);
drop(_5) -> [return: bb5, unwind: bb10];
}
bb5: {
StorageDead(_5);
- drop(_4) -> [return: bb6, unwind: bb11];
+ goto -> bb6;
}
bb6: {
+ _8 = const false;
StorageDead(_4);
StorageDead(_3);
drop(_2) -> [return: bb7, unwind: bb12];
}
bb7: {
StorageDead(_2);
tailcall g();
}
bb8 (cleanup): {
- drop(_7) -> [return: bb9, unwind terminate(cleanup)];
+ goto -> bb9;
}
bb9 (cleanup): {
drop(_5) -> [return: bb10, unwind terminate(cleanup)];
}
bb10 (cleanup): {
- drop(_4) -> [return: bb11, unwind terminate(cleanup)];
+ goto -> bb14;
}
bb11 (cleanup): {
drop(_2) -> [return: bb12, unwind terminate(cleanup)];
}
bb12 (cleanup): {
resume;
+ }
+
+ bb13 (cleanup): {
+ drop(_4) -> [return: bb11, unwind terminate(cleanup)];
+ }
+
+ bb14 (cleanup): {
+ switchInt(_8) -> [0: bb11, otherwise: bb13];
}
}

View File

@ -0,0 +1,109 @@
- // MIR for `f` before ElaborateDrops
+ // MIR for `f` after ElaborateDrops
fn f() -> () {
let mut _0: ();
let mut _1: !;
let _2: std::string::String;
let _6: ();
let mut _7: std::string::String;
+ let mut _8: bool;
scope 1 {
debug _a => _2;
let _3: i32;
scope 2 {
debug _b => _3;
let _4: std::string::String;
scope 3 {
debug _c => _4;
let _5: std::string::String;
scope 4 {
debug _d => _5;
}
}
}
}
bb0: {
+ _8 = const false;
StorageLive(_2);
_2 = String::new() -> [return: bb1, unwind continue];
}
bb1: {
StorageLive(_3);
_3 = const 12_i32;
StorageLive(_4);
_4 = String::new() -> [return: bb2, unwind: bb11];
}
bb2: {
+ _8 = const true;
StorageLive(_5);
_5 = String::new() -> [return: bb3, unwind: bb10];
}
bb3: {
StorageLive(_6);
StorageLive(_7);
+ _8 = const false;
_7 = move _4;
_6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb8];
}
bb4: {
StorageDead(_7);
StorageDead(_6);
drop(_5) -> [return: bb5, unwind: bb10];
}
bb5: {
StorageDead(_5);
- drop(_4) -> [return: bb6, unwind: bb11];
+ goto -> bb6;
}
bb6: {
+ _8 = const false;
StorageDead(_4);
StorageDead(_3);
- drop(_2) -> [return: bb7, unwind continue];
+ drop(_2) -> [return: bb7, unwind: bb12];
}
bb7: {
StorageDead(_2);
tailcall g();
}
bb8 (cleanup): {
- drop(_7) -> [return: bb9, unwind terminate(cleanup)];
+ goto -> bb9;
}
bb9 (cleanup): {
drop(_5) -> [return: bb10, unwind terminate(cleanup)];
}
bb10 (cleanup): {
- drop(_4) -> [return: bb11, unwind terminate(cleanup)];
+ goto -> bb14;
}
bb11 (cleanup): {
drop(_2) -> [return: bb12, unwind terminate(cleanup)];
}
bb12 (cleanup): {
resume;
+ }
+
+ bb13 (cleanup): {
+ drop(_4) -> [return: bb11, unwind terminate(cleanup)];
+ }
+
+ bb14 (cleanup): {
+ switchInt(_8) -> [0: bb11, otherwise: bb13];
}
}

View File

@ -0,0 +1,118 @@
// MIR for `f` after built
fn f() -> () {
let mut _0: ();
let mut _1: !;
let _2: std::string::String;
let _6: ();
let mut _7: std::string::String;
scope 1 {
debug _a => _2;
let _3: i32;
scope 2 {
debug _b => _3;
let _4: std::string::String;
scope 3 {
debug _c => _4;
let _5: std::string::String;
scope 4 {
debug _d => _5;
}
}
}
}
bb0: {
StorageLive(_2);
_2 = String::new() -> [return: bb1, unwind: bb17];
}
bb1: {
FakeRead(ForLet(None), _2);
StorageLive(_3);
_3 = const 12_i32;
FakeRead(ForLet(None), _3);
StorageLive(_4);
_4 = String::new() -> [return: bb2, unwind: bb16];
}
bb2: {
FakeRead(ForLet(None), _4);
StorageLive(_5);
_5 = String::new() -> [return: bb3, unwind: bb15];
}
bb3: {
FakeRead(ForLet(None), _5);
StorageLive(_6);
StorageLive(_7);
_7 = move _4;
_6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb13];
}
bb4: {
StorageDead(_7);
StorageDead(_6);
drop(_5) -> [return: bb5, unwind: bb15];
}
bb5: {
StorageDead(_5);
drop(_4) -> [return: bb6, unwind: bb16];
}
bb6: {
StorageDead(_4);
StorageDead(_3);
drop(_2) -> [return: bb7, unwind: bb17];
}
bb7: {
StorageDead(_2);
tailcall g();
}
bb8: {
drop(_5) -> [return: bb9, unwind: bb15];
}
bb9: {
StorageDead(_5);
drop(_4) -> [return: bb10, unwind: bb16];
}
bb10: {
StorageDead(_4);
StorageDead(_3);
drop(_2) -> [return: bb11, unwind: bb17];
}
bb11: {
StorageDead(_2);
unreachable;
}
bb12: {
return;
}
bb13 (cleanup): {
drop(_7) -> [return: bb14, unwind terminate(cleanup)];
}
bb14 (cleanup): {
drop(_5) -> [return: bb15, unwind terminate(cleanup)];
}
bb15 (cleanup): {
drop(_4) -> [return: bb16, unwind terminate(cleanup)];
}
bb16 (cleanup): {
drop(_2) -> [return: bb17, unwind terminate(cleanup)];
}
bb17 (cleanup): {
resume;
}
}

View File

@ -0,0 +1,118 @@
// MIR for `f` after built
fn f() -> () {
let mut _0: ();
let mut _1: !;
let _2: std::string::String;
let _6: ();
let mut _7: std::string::String;
scope 1 {
debug _a => _2;
let _3: i32;
scope 2 {
debug _b => _3;
let _4: std::string::String;
scope 3 {
debug _c => _4;
let _5: std::string::String;
scope 4 {
debug _d => _5;
}
}
}
}
bb0: {
StorageLive(_2);
_2 = String::new() -> [return: bb1, unwind: bb17];
}
bb1: {
FakeRead(ForLet(None), _2);
StorageLive(_3);
_3 = const 12_i32;
FakeRead(ForLet(None), _3);
StorageLive(_4);
_4 = String::new() -> [return: bb2, unwind: bb16];
}
bb2: {
FakeRead(ForLet(None), _4);
StorageLive(_5);
_5 = String::new() -> [return: bb3, unwind: bb15];
}
bb3: {
FakeRead(ForLet(None), _5);
StorageLive(_6);
StorageLive(_7);
_7 = move _4;
_6 = std::mem::drop::<String>(move _7) -> [return: bb4, unwind: bb13];
}
bb4: {
StorageDead(_7);
StorageDead(_6);
drop(_5) -> [return: bb5, unwind: bb15];
}
bb5: {
StorageDead(_5);
drop(_4) -> [return: bb6, unwind: bb16];
}
bb6: {
StorageDead(_4);
StorageDead(_3);
drop(_2) -> [return: bb7, unwind: bb17];
}
bb7: {
StorageDead(_2);
tailcall g();
}
bb8: {
drop(_5) -> [return: bb9, unwind: bb15];
}
bb9: {
StorageDead(_5);
drop(_4) -> [return: bb10, unwind: bb16];
}
bb10: {
StorageDead(_4);
StorageDead(_3);
drop(_2) -> [return: bb11, unwind: bb17];
}
bb11: {
StorageDead(_2);
unreachable;
}
bb12: {
return;
}
bb13 (cleanup): {
drop(_7) -> [return: bb14, unwind terminate(cleanup)];
}
bb14 (cleanup): {
drop(_5) -> [return: bb15, unwind terminate(cleanup)];
}
bb15 (cleanup): {
drop(_4) -> [return: bb16, unwind terminate(cleanup)];
}
bb16 (cleanup): {
drop(_2) -> [return: bb17, unwind terminate(cleanup)];
}
bb17 (cleanup): {
resume;
}
}

View File

@ -0,0 +1,184 @@
- // MIR for `f_with_arg` before ElaborateDrops
+ // MIR for `f_with_arg` after ElaborateDrops
fn f_with_arg(_1: String, _2: String) -> () {
debug _arg1 => _1;
debug _arg2 => _2;
let mut _0: ();
let mut _3: !;
let _4: std::string::String;
let _8: ();
let mut _9: std::string::String;
let mut _10: std::string::String;
let mut _11: std::string::String;
+ let mut _12: bool;
scope 1 {
debug _a => _4;
let _5: i32;
scope 2 {
debug _b => _5;
let _6: std::string::String;
scope 3 {
debug _c => _6;
let _7: std::string::String;
scope 4 {
debug _d => _7;
}
}
}
}
bb0: {
+ _12 = const false;
StorageLive(_4);
_4 = String::new() -> [return: bb1, unwind: bb27];
}
bb1: {
StorageLive(_5);
_5 = const 12_i32;
StorageLive(_6);
_6 = String::new() -> [return: bb2, unwind: bb26];
}
bb2: {
+ _12 = const true;
StorageLive(_7);
_7 = String::new() -> [return: bb3, unwind: bb25];
}
bb3: {
StorageLive(_8);
StorageLive(_9);
+ _12 = const false;
_9 = move _6;
_8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb23];
}
bb4: {
StorageDead(_9);
StorageDead(_8);
StorageLive(_10);
_10 = String::new() -> [return: bb5, unwind: bb24];
}
bb5: {
StorageLive(_11);
_11 = String::new() -> [return: bb6, unwind: bb22];
}
bb6: {
drop(_7) -> [return: bb7, unwind: bb20];
}
bb7: {
StorageDead(_7);
- drop(_6) -> [return: bb8, unwind: bb18];
+ goto -> bb8;
}
bb8: {
+ _12 = const false;
StorageDead(_6);
StorageDead(_5);
drop(_4) -> [return: bb9, unwind: bb16];
}
bb9: {
StorageDead(_4);
drop(_2) -> [return: bb10, unwind: bb14];
}
bb10: {
drop(_1) -> [return: bb11, unwind: bb12];
}
bb11: {
tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) });
}
bb12 (cleanup): {
drop(_10) -> [return: bb13, unwind terminate(cleanup)];
}
bb13 (cleanup): {
drop(_11) -> [return: bb29, unwind terminate(cleanup)];
}
bb14 (cleanup): {
drop(_10) -> [return: bb15, unwind terminate(cleanup)];
}
bb15 (cleanup): {
drop(_11) -> [return: bb28, unwind terminate(cleanup)];
}
bb16 (cleanup): {
drop(_10) -> [return: bb17, unwind terminate(cleanup)];
}
bb17 (cleanup): {
drop(_11) -> [return: bb27, unwind terminate(cleanup)];
}
bb18 (cleanup): {
- drop(_10) -> [return: bb19, unwind terminate(cleanup)];
+ goto -> bb19;
}
bb19 (cleanup): {
- drop(_11) -> [return: bb26, unwind terminate(cleanup)];
+ goto -> bb26;
}
bb20 (cleanup): {
drop(_10) -> [return: bb21, unwind terminate(cleanup)];
}
bb21 (cleanup): {
drop(_11) -> [return: bb25, unwind terminate(cleanup)];
}
bb22 (cleanup): {
drop(_10) -> [return: bb24, unwind terminate(cleanup)];
}
bb23 (cleanup): {
- drop(_9) -> [return: bb24, unwind terminate(cleanup)];
+ goto -> bb24;
}
bb24 (cleanup): {
drop(_7) -> [return: bb25, unwind terminate(cleanup)];
}
bb25 (cleanup): {
- drop(_6) -> [return: bb26, unwind terminate(cleanup)];
+ goto -> bb31;
}
bb26 (cleanup): {
drop(_4) -> [return: bb27, unwind terminate(cleanup)];
}
bb27 (cleanup): {
drop(_2) -> [return: bb28, unwind terminate(cleanup)];
}
bb28 (cleanup): {
drop(_1) -> [return: bb29, unwind terminate(cleanup)];
}
bb29 (cleanup): {
resume;
+ }
+
+ bb30 (cleanup): {
+ drop(_6) -> [return: bb26, unwind terminate(cleanup)];
+ }
+
+ bb31 (cleanup): {
+ switchInt(_12) -> [0: bb26, otherwise: bb30];
}
}

View File

@ -0,0 +1,184 @@
- // MIR for `f_with_arg` before ElaborateDrops
+ // MIR for `f_with_arg` after ElaborateDrops
fn f_with_arg(_1: String, _2: String) -> () {
debug _arg1 => _1;
debug _arg2 => _2;
let mut _0: ();
let mut _3: !;
let _4: std::string::String;
let _8: ();
let mut _9: std::string::String;
let mut _10: std::string::String;
let mut _11: std::string::String;
+ let mut _12: bool;
scope 1 {
debug _a => _4;
let _5: i32;
scope 2 {
debug _b => _5;
let _6: std::string::String;
scope 3 {
debug _c => _6;
let _7: std::string::String;
scope 4 {
debug _d => _7;
}
}
}
}
bb0: {
+ _12 = const false;
StorageLive(_4);
_4 = String::new() -> [return: bb1, unwind: bb27];
}
bb1: {
StorageLive(_5);
_5 = const 12_i32;
StorageLive(_6);
_6 = String::new() -> [return: bb2, unwind: bb26];
}
bb2: {
+ _12 = const true;
StorageLive(_7);
_7 = String::new() -> [return: bb3, unwind: bb25];
}
bb3: {
StorageLive(_8);
StorageLive(_9);
+ _12 = const false;
_9 = move _6;
_8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb23];
}
bb4: {
StorageDead(_9);
StorageDead(_8);
StorageLive(_10);
_10 = String::new() -> [return: bb5, unwind: bb24];
}
bb5: {
StorageLive(_11);
_11 = String::new() -> [return: bb6, unwind: bb22];
}
bb6: {
drop(_7) -> [return: bb7, unwind: bb20];
}
bb7: {
StorageDead(_7);
- drop(_6) -> [return: bb8, unwind: bb18];
+ goto -> bb8;
}
bb8: {
+ _12 = const false;
StorageDead(_6);
StorageDead(_5);
drop(_4) -> [return: bb9, unwind: bb16];
}
bb9: {
StorageDead(_4);
drop(_2) -> [return: bb10, unwind: bb14];
}
bb10: {
drop(_1) -> [return: bb11, unwind: bb12];
}
bb11: {
tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) });
}
bb12 (cleanup): {
drop(_10) -> [return: bb13, unwind terminate(cleanup)];
}
bb13 (cleanup): {
drop(_11) -> [return: bb29, unwind terminate(cleanup)];
}
bb14 (cleanup): {
drop(_10) -> [return: bb15, unwind terminate(cleanup)];
}
bb15 (cleanup): {
drop(_11) -> [return: bb28, unwind terminate(cleanup)];
}
bb16 (cleanup): {
drop(_10) -> [return: bb17, unwind terminate(cleanup)];
}
bb17 (cleanup): {
drop(_11) -> [return: bb27, unwind terminate(cleanup)];
}
bb18 (cleanup): {
- drop(_10) -> [return: bb19, unwind terminate(cleanup)];
+ goto -> bb19;
}
bb19 (cleanup): {
- drop(_11) -> [return: bb26, unwind terminate(cleanup)];
+ goto -> bb26;
}
bb20 (cleanup): {
drop(_10) -> [return: bb21, unwind terminate(cleanup)];
}
bb21 (cleanup): {
drop(_11) -> [return: bb25, unwind terminate(cleanup)];
}
bb22 (cleanup): {
drop(_10) -> [return: bb24, unwind terminate(cleanup)];
}
bb23 (cleanup): {
- drop(_9) -> [return: bb24, unwind terminate(cleanup)];
+ goto -> bb24;
}
bb24 (cleanup): {
drop(_7) -> [return: bb25, unwind terminate(cleanup)];
}
bb25 (cleanup): {
- drop(_6) -> [return: bb26, unwind terminate(cleanup)];
+ goto -> bb31;
}
bb26 (cleanup): {
drop(_4) -> [return: bb27, unwind terminate(cleanup)];
}
bb27 (cleanup): {
drop(_2) -> [return: bb28, unwind terminate(cleanup)];
}
bb28 (cleanup): {
drop(_1) -> [return: bb29, unwind terminate(cleanup)];
}
bb29 (cleanup): {
resume;
+ }
+
+ bb30 (cleanup): {
+ drop(_6) -> [return: bb26, unwind terminate(cleanup)];
+ }
+
+ bb31 (cleanup): {
+ switchInt(_12) -> [0: bb26, otherwise: bb30];
}
}

View File

@ -0,0 +1,202 @@
// MIR for `f_with_arg` after built
fn f_with_arg(_1: String, _2: String) -> () {
debug _arg1 => _1;
debug _arg2 => _2;
let mut _0: ();
let mut _3: !;
let _4: std::string::String;
let _8: ();
let mut _9: std::string::String;
let mut _10: std::string::String;
let mut _11: std::string::String;
scope 1 {
debug _a => _4;
let _5: i32;
scope 2 {
debug _b => _5;
let _6: std::string::String;
scope 3 {
debug _c => _6;
let _7: std::string::String;
scope 4 {
debug _d => _7;
}
}
}
}
bb0: {
StorageLive(_4);
_4 = String::new() -> [return: bb1, unwind: bb34];
}
bb1: {
FakeRead(ForLet(None), _4);
StorageLive(_5);
_5 = const 12_i32;
FakeRead(ForLet(None), _5);
StorageLive(_6);
_6 = String::new() -> [return: bb2, unwind: bb33];
}
bb2: {
FakeRead(ForLet(None), _6);
StorageLive(_7);
_7 = String::new() -> [return: bb3, unwind: bb32];
}
bb3: {
FakeRead(ForLet(None), _7);
StorageLive(_8);
StorageLive(_9);
_9 = move _6;
_8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb30];
}
bb4: {
StorageDead(_9);
StorageDead(_8);
StorageLive(_10);
_10 = String::new() -> [return: bb5, unwind: bb31];
}
bb5: {
StorageLive(_11);
_11 = String::new() -> [return: bb6, unwind: bb29];
}
bb6: {
drop(_7) -> [return: bb7, unwind: bb27];
}
bb7: {
StorageDead(_7);
drop(_6) -> [return: bb8, unwind: bb25];
}
bb8: {
StorageDead(_6);
StorageDead(_5);
drop(_4) -> [return: bb9, unwind: bb23];
}
bb9: {
StorageDead(_4);
drop(_2) -> [return: bb10, unwind: bb21];
}
bb10: {
drop(_1) -> [return: bb11, unwind: bb19];
}
bb11: {
tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) });
}
bb12: {
StorageDead(_11);
StorageDead(_10);
drop(_7) -> [return: bb13, unwind: bb32];
}
bb13: {
StorageDead(_7);
drop(_6) -> [return: bb14, unwind: bb33];
}
bb14: {
StorageDead(_6);
StorageDead(_5);
drop(_4) -> [return: bb15, unwind: bb34];
}
bb15: {
StorageDead(_4);
unreachable;
}
bb16: {
drop(_2) -> [return: bb17, unwind: bb35];
}
bb17: {
drop(_1) -> [return: bb18, unwind: bb36];
}
bb18: {
return;
}
bb19 (cleanup): {
drop(_10) -> [return: bb20, unwind terminate(cleanup)];
}
bb20 (cleanup): {
drop(_11) -> [return: bb36, unwind terminate(cleanup)];
}
bb21 (cleanup): {
drop(_10) -> [return: bb22, unwind terminate(cleanup)];
}
bb22 (cleanup): {
drop(_11) -> [return: bb35, unwind terminate(cleanup)];
}
bb23 (cleanup): {
drop(_10) -> [return: bb24, unwind terminate(cleanup)];
}
bb24 (cleanup): {
drop(_11) -> [return: bb34, unwind terminate(cleanup)];
}
bb25 (cleanup): {
drop(_10) -> [return: bb26, unwind terminate(cleanup)];
}
bb26 (cleanup): {
drop(_11) -> [return: bb33, unwind terminate(cleanup)];
}
bb27 (cleanup): {
drop(_10) -> [return: bb28, unwind terminate(cleanup)];
}
bb28 (cleanup): {
drop(_11) -> [return: bb32, unwind terminate(cleanup)];
}
bb29 (cleanup): {
drop(_10) -> [return: bb31, unwind terminate(cleanup)];
}
bb30 (cleanup): {
drop(_9) -> [return: bb31, unwind terminate(cleanup)];
}
bb31 (cleanup): {
drop(_7) -> [return: bb32, unwind terminate(cleanup)];
}
bb32 (cleanup): {
drop(_6) -> [return: bb33, unwind terminate(cleanup)];
}
bb33 (cleanup): {
drop(_4) -> [return: bb34, unwind terminate(cleanup)];
}
bb34 (cleanup): {
drop(_2) -> [return: bb35, unwind terminate(cleanup)];
}
bb35 (cleanup): {
drop(_1) -> [return: bb36, unwind terminate(cleanup)];
}
bb36 (cleanup): {
resume;
}
}

View File

@ -0,0 +1,202 @@
// MIR for `f_with_arg` after built
fn f_with_arg(_1: String, _2: String) -> () {
debug _arg1 => _1;
debug _arg2 => _2;
let mut _0: ();
let mut _3: !;
let _4: std::string::String;
let _8: ();
let mut _9: std::string::String;
let mut _10: std::string::String;
let mut _11: std::string::String;
scope 1 {
debug _a => _4;
let _5: i32;
scope 2 {
debug _b => _5;
let _6: std::string::String;
scope 3 {
debug _c => _6;
let _7: std::string::String;
scope 4 {
debug _d => _7;
}
}
}
}
bb0: {
StorageLive(_4);
_4 = String::new() -> [return: bb1, unwind: bb34];
}
bb1: {
FakeRead(ForLet(None), _4);
StorageLive(_5);
_5 = const 12_i32;
FakeRead(ForLet(None), _5);
StorageLive(_6);
_6 = String::new() -> [return: bb2, unwind: bb33];
}
bb2: {
FakeRead(ForLet(None), _6);
StorageLive(_7);
_7 = String::new() -> [return: bb3, unwind: bb32];
}
bb3: {
FakeRead(ForLet(None), _7);
StorageLive(_8);
StorageLive(_9);
_9 = move _6;
_8 = std::mem::drop::<String>(move _9) -> [return: bb4, unwind: bb30];
}
bb4: {
StorageDead(_9);
StorageDead(_8);
StorageLive(_10);
_10 = String::new() -> [return: bb5, unwind: bb31];
}
bb5: {
StorageLive(_11);
_11 = String::new() -> [return: bb6, unwind: bb29];
}
bb6: {
drop(_7) -> [return: bb7, unwind: bb27];
}
bb7: {
StorageDead(_7);
drop(_6) -> [return: bb8, unwind: bb25];
}
bb8: {
StorageDead(_6);
StorageDead(_5);
drop(_4) -> [return: bb9, unwind: bb23];
}
bb9: {
StorageDead(_4);
drop(_2) -> [return: bb10, unwind: bb21];
}
bb10: {
drop(_1) -> [return: bb11, unwind: bb19];
}
bb11: {
tailcall g_with_arg(Spanned { node: move _10, span: $DIR/tail_call_drops.rs:36:23: 36:36 (#0) }, Spanned { node: move _11, span: $DIR/tail_call_drops.rs:36:38: 36:51 (#0) });
}
bb12: {
StorageDead(_11);
StorageDead(_10);
drop(_7) -> [return: bb13, unwind: bb32];
}
bb13: {
StorageDead(_7);
drop(_6) -> [return: bb14, unwind: bb33];
}
bb14: {
StorageDead(_6);
StorageDead(_5);
drop(_4) -> [return: bb15, unwind: bb34];
}
bb15: {
StorageDead(_4);
unreachable;
}
bb16: {
drop(_2) -> [return: bb17, unwind: bb35];
}
bb17: {
drop(_1) -> [return: bb18, unwind: bb36];
}
bb18: {
return;
}
bb19 (cleanup): {
drop(_10) -> [return: bb20, unwind terminate(cleanup)];
}
bb20 (cleanup): {
drop(_11) -> [return: bb36, unwind terminate(cleanup)];
}
bb21 (cleanup): {
drop(_10) -> [return: bb22, unwind terminate(cleanup)];
}
bb22 (cleanup): {
drop(_11) -> [return: bb35, unwind terminate(cleanup)];
}
bb23 (cleanup): {
drop(_10) -> [return: bb24, unwind terminate(cleanup)];
}
bb24 (cleanup): {
drop(_11) -> [return: bb34, unwind terminate(cleanup)];
}
bb25 (cleanup): {
drop(_10) -> [return: bb26, unwind terminate(cleanup)];
}
bb26 (cleanup): {
drop(_11) -> [return: bb33, unwind terminate(cleanup)];
}
bb27 (cleanup): {
drop(_10) -> [return: bb28, unwind terminate(cleanup)];
}
bb28 (cleanup): {
drop(_11) -> [return: bb32, unwind terminate(cleanup)];
}
bb29 (cleanup): {
drop(_10) -> [return: bb31, unwind terminate(cleanup)];
}
bb30 (cleanup): {
drop(_9) -> [return: bb31, unwind terminate(cleanup)];
}
bb31 (cleanup): {
drop(_7) -> [return: bb32, unwind terminate(cleanup)];
}
bb32 (cleanup): {
drop(_6) -> [return: bb33, unwind terminate(cleanup)];
}
bb33 (cleanup): {
drop(_4) -> [return: bb34, unwind terminate(cleanup)];
}
bb34 (cleanup): {
drop(_2) -> [return: bb35, unwind terminate(cleanup)];
}
bb35 (cleanup): {
drop(_1) -> [return: bb36, unwind terminate(cleanup)];
}
bb36 (cleanup): {
resume;
}
}

View File

@ -0,0 +1,41 @@
// skip-filecheck
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
#![allow(incomplete_features)]
#![feature(explicit_tail_calls)]
// EMIT_MIR tail_call_drops.f.built.after.mir
// Expected result:
// drop(_d) -> drop(_c) -> drop(_a) -> tailcall g()
//
// EMIT_MIR tail_call_drops.f.ElaborateDrops.diff
// Expected result:
// drop(_d) -> drop(_a) -> tailcall g()
fn f() {
let _a = String::new();
let _b = 12;
let _c = String::new();
let _d = String::new();
drop(_c);
become g();
}
fn g() {}
// EMIT_MIR tail_call_drops.f_with_arg.built.after.mir
// EMIT_MIR tail_call_drops.f_with_arg.ElaborateDrops.diff
fn f_with_arg(_arg1: String, _arg2: String) {
let _a = String::new();
let _b = 12;
let _c = String::new();
let _d = String::new();
drop(_c);
become g_with_arg(String::new(), String::new());
}
fn g_with_arg(_arg1: String, _arg2: String) {}
fn main() {}

View File

@ -0,0 +1,70 @@
// FIXME(explicit_tail_calls): enable this test once rustc_codegen_ssa supports tail calls
//@ ignore-test: tail calls are not implemented in rustc_codegen_ssa yet, so this causes 🧊
//@ run-pass
#![allow(incomplete_features)]
#![feature(explicit_tail_calls)]
use std::cell::RefCell;
fn main() {
let tail_counter = Default::default();
tail_recursive(0, &tail_counter);
assert_eq!(tail_counter.into_inner(), (0..128).collect::<Vec<u8>>());
let simply_counter = Default::default();
simply_recursive(0, &simply_counter);
assert_eq!(simply_counter.into_inner(), (0..128).rev().collect::<Vec<u8>>());
let scope_counter = Default::default();
out_of_inner_scope(&scope_counter);
assert_eq!(scope_counter.into_inner(), (0..8).collect::<Vec<u8>>());
}
fn tail_recursive(n: u8, order: &RefCell<Vec<u8>>) {
if n >= 128 {
return;
}
let _local = DropCounter(n, order);
become tail_recursive(n + 1, order)
}
fn simply_recursive(n: u8, order: &RefCell<Vec<u8>>) {
if n >= 128 {
return;
}
let _local = DropCounter(n, order);
return simply_recursive(n + 1, order);
}
fn out_of_inner_scope(order: &RefCell<Vec<u8>>) {
fn inner(order: &RefCell<Vec<u8>>) {
let _7 = DropCounter(7, order);
let _6 = DropCounter(6, order);
}
let _5 = DropCounter(5, order);
let _4 = DropCounter(4, order);
if true {
let _3 = DropCounter(3, order);
let _2 = DropCounter(2, order);
loop {
let _1 = DropCounter(1, order);
let _0 = DropCounter(0, order);
become inner(order);
}
}
}
struct DropCounter<'a>(u8, &'a RefCell<Vec<u8>>);
impl Drop for DropCounter<'_> {
#[track_caller]
fn drop(&mut self) {
self.1.borrow_mut().push(self.0);
}
}