diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs index 60ab843257d..7d2c32e000b 100644 --- a/compiler/rustc_mir_build/src/build/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs @@ -95,7 +95,7 @@ pub(crate) fn stmt_expr( } 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 @@ pub(crate) fn stmt_expr( 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!( diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 9e7534a283d..948301e2ece 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -745,6 +745,91 @@ pub(crate) fn break_for_else(&mut self, block: BasicBlock, source_info: SourceIn 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>], + 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. diff --git a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff new file mode 100644 index 00000000000..44673ea00a9 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-abort.diff @@ -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::(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]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff new file mode 100644 index 00000000000..a6d33a24595 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.ElaborateDrops.panic-unwind.diff @@ -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::(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]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f.built.after.panic-abort.mir b/tests/mir-opt/tail_call_drops.f.built.after.panic-abort.mir new file mode 100644 index 00000000000..2c3d62491d7 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.built.after.panic-abort.mir @@ -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::(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; + } +} diff --git a/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir b/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir new file mode 100644 index 00000000000..2c3d62491d7 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f.built.after.panic-unwind.mir @@ -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::(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; + } +} diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-abort.diff b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-abort.diff new file mode 100644 index 00000000000..c7df2bb2207 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-abort.diff @@ -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::(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]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff new file mode 100644 index 00000000000..c7df2bb2207 --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.ElaborateDrops.panic-unwind.diff @@ -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::(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]; + } + } + diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-abort.mir b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-abort.mir new file mode 100644 index 00000000000..744f1989acc --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-abort.mir @@ -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::(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; + } +} diff --git a/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir new file mode 100644 index 00000000000..744f1989acc --- /dev/null +++ b/tests/mir-opt/tail_call_drops.f_with_arg.built.after.panic-unwind.mir @@ -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::(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; + } +} diff --git a/tests/mir-opt/tail_call_drops.rs b/tests/mir-opt/tail_call_drops.rs new file mode 100644 index 00000000000..56f4852a95f --- /dev/null +++ b/tests/mir-opt/tail_call_drops.rs @@ -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() {} diff --git a/tests/ui/explicit-tail-calls/drop-order.rs b/tests/ui/explicit-tail-calls/drop-order.rs new file mode 100644 index 00000000000..e20730446ec --- /dev/null +++ b/tests/ui/explicit-tail-calls/drop-order.rs @@ -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::>()); + + let simply_counter = Default::default(); + simply_recursive(0, &simply_counter); + assert_eq!(simply_counter.into_inner(), (0..128).rev().collect::>()); + + let scope_counter = Default::default(); + out_of_inner_scope(&scope_counter); + assert_eq!(scope_counter.into_inner(), (0..8).collect::>()); +} + +fn tail_recursive(n: u8, order: &RefCell>) { + if n >= 128 { + return; + } + + let _local = DropCounter(n, order); + + become tail_recursive(n + 1, order) +} + +fn simply_recursive(n: u8, order: &RefCell>) { + if n >= 128 { + return; + } + + let _local = DropCounter(n, order); + + return simply_recursive(n + 1, order); +} + +fn out_of_inner_scope(order: &RefCell>) { + fn inner(order: &RefCell>) { + 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>); + +impl Drop for DropCounter<'_> { + #[track_caller] + fn drop(&mut self) { + self.1.borrow_mut().push(self.0); + } +}