diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index f9440866e4925..7353ca9285ddb 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -1,22 +1,18 @@ use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; use crate::build::ForGuard::OutsideGuard; use crate::build::matches::ArmHasGuard; -use crate::build::scope::DropKind; use crate::hair::*; -use rustc::middle::region; use rustc::mir::*; use rustc::hir; use syntax_pos::Span; impl<'a, 'tcx> Builder<'a, 'tcx> { - pub fn ast_block( - &mut self, - destination: &Place<'tcx>, - scope: Option, - block: BasicBlock, - ast_block: &'tcx hir::Block, - source_info: SourceInfo, - ) -> BlockAnd<()> { + pub fn ast_block(&mut self, + destination: &Place<'tcx>, + block: BasicBlock, + ast_block: &'tcx hir::Block, + source_info: SourceInfo) + -> BlockAnd<()> { let Block { region_scope, opt_destruction_scope, @@ -25,61 +21,37 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr, targeted_by_break, safety_mode - } = self.hir.mirror(ast_block); + } = + self.hir.mirror(ast_block); self.in_opt_scope(opt_destruction_scope.map(|de|(de, source_info)), move |this| { this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| { if targeted_by_break { // This is a `break`-able block let exit_block = this.cfg.start_new_block(); - if let Some(scope) = scope { - // Breakable blocks assign to their destination on each - // `break`, as well as when they exit normally. So we - // can't schedule the drop in the last expression like - // normal blocks do. - let local = destination.as_local() - .expect("cannot schedule drop of non-Local place"); - this.schedule_drop(span, scope, local, DropKind::Value); - } let block_exit = this.in_breakable_scope( None, exit_block, destination.clone(), |this| { - this.ast_block_stmts( - destination, - None, - block, - span, - stmts, - expr, - safety_mode, - ) + this.ast_block_stmts(destination, block, span, stmts, expr, + safety_mode) }); this.cfg.terminate(unpack!(block_exit), source_info, TerminatorKind::Goto { target: exit_block }); exit_block.unit() } else { - this.ast_block_stmts( - destination, - scope, - block, - span, - stmts, - expr, - safety_mode, - ) + this.ast_block_stmts(destination, block, span, stmts, expr, + safety_mode) } }) }) } - fn ast_block_stmts( - &mut self, - destination: &Place<'tcx>, - scope: Option, - mut block: BasicBlock, - span: Span, - stmts: Vec>, - expr: Option>, - safety_mode: BlockSafety, - ) -> BlockAnd<()> { + fn ast_block_stmts(&mut self, + destination: &Place<'tcx>, + mut block: BasicBlock, + span: Span, + stmts: Vec>, + expr: Option>, + safety_mode: BlockSafety) + -> BlockAnd<()> { let this = self; // This convoluted structure is to avoid using recursion as we walk down a list @@ -205,7 +177,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.block_context.currently_ignores_tail_results(); this.block_context.push(BlockFrame::TailExpr { tail_result_is_ignored }); - unpack!(block = this.into(destination, scope, block, expr)); + unpack!(block = this.into(destination, block, expr)); let popped = this.block_context.pop(); assert!(popped.map_or(false, |bf|bf.is_tail_expr())); diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index bd9edc56872b9..87d95a751534d 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -136,14 +136,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.cfg .push_assign(block, source_info, &Place::from(result), box_); - // Initialize the box contents. No scope is needed since the - // `Box` is already scheduled to be dropped. + // initialize the box contents: unpack!( block = this.into( &Place::from(result).deref(), - None, - block, - value + block, value ) ); block.and(Rvalue::Use(Operand::Move(Place::from(result)))) diff --git a/src/librustc_mir/build/expr/as_temp.rs b/src/librustc_mir/build/expr/as_temp.rs index bd20f27c945c1..18332ed68f8bd 100644 --- a/src/librustc_mir/build/expr/as_temp.rs +++ b/src/librustc_mir/build/expr/as_temp.rs @@ -109,7 +109,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - unpack!(block = this.into(temp_place, temp_lifetime, block, expr)); + unpack!(block = this.into(temp_place, block, expr)); + + if let Some(temp_lifetime) = temp_lifetime { + this.schedule_drop( + expr_span, + temp_lifetime, + temp, + DropKind::Value, + ); + } block.and(temp) } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index c262215ab9be4..8a6bc5a2a764e 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -2,9 +2,7 @@ use crate::build::expr::category::{Category, RvalueFunc}; use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder}; -use crate::build::scope::DropKind; use crate::hair::*; -use rustc::middle::region; use rustc::mir::*; use rustc::ty; @@ -13,18 +11,15 @@ use rustc_target::spec::abi::Abi; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Compile `expr`, storing the result into `destination`, which /// is assumed to be uninitialized. - /// If a `drop_scope` is provided, `destination` is scheduled to be dropped - /// in `scope` once it has been initialized. pub fn into_expr( &mut self, destination: &Place<'tcx>, - scope: Option, mut block: BasicBlock, expr: Expr<'tcx>, ) -> BlockAnd<()> { debug!( - "into_expr(destination={:?}, scope={:?}, block={:?}, expr={:?})", - destination, scope, block, expr + "into_expr(destination={:?}, block={:?}, expr={:?})", + destination, block, expr ); // since we frequently have to reference `self` from within a @@ -40,14 +35,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => false, }; - let schedule_drop = move |this: &mut Self| { - if let Some(drop_scope) = scope { - let local = destination.as_local() - .expect("cannot schedule drop of non-Local place"); - this.schedule_drop(expr_span, drop_scope, local, DropKind::Value); - } - }; - if !expr_is_block_or_scope { this.block_context.push(BlockFrame::SubExpr); } @@ -60,14 +47,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } => { let region_scope = (region_scope, source_info); this.in_scope(region_scope, lint_level, |this| { - this.into(destination, scope, block, value) + this.into(destination, block, value) }) } ExprKind::Block { body: ast_block } => { - this.ast_block(destination, scope, block, ast_block, source_info) + this.ast_block(destination, block, ast_block, source_info) } ExprKind::Match { scrutinee, arms } => { - this.match_expr(destination, scope, expr_span, block, scrutinee, arms) + this.match_expr(destination, expr_span, block, scrutinee, arms) } ExprKind::NeverToAny { source } => { let source = this.hir.mirror(source); @@ -80,7 +67,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // This is an optimization. If the expression was a call then we already have an // unreachable block. Don't bother to terminate it and create a new one. - schedule_drop(this); if is_call { block.unit() } else { @@ -178,9 +164,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { TerminatorKind::Goto { target: loop_block }, ); - // Loops assign to their destination on each `break`. Since we - // can't easily unschedule drops, we schedule the drop now. - schedule_drop(this); this.in_breakable_scope( Some(loop_block), exit_block, @@ -202,8 +185,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // introduce a unit temporary as the destination for the loop body. let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. - // No scope is provided, since we've scheduled the drop above. - let body_block_end = unpack!(this.into(&tmp, None, body_block, body)); + let body_block_end = unpack!(this.into(&tmp, body_block, body)); this.cfg.terminate( body_block_end, source_info, @@ -252,14 +234,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { is_block_tail: None, }); let ptr_temp = Place::from(ptr_temp); - // No need for a scope, ptr_temp doesn't need drop - let block = unpack!(this.into(&ptr_temp, None, block, ptr)); - // Maybe we should provide a scope here so that - // `move_val_init` wouldn't leak on panic even with an - // arbitrary `val` expression, but `schedule_drop`, - // borrowck and drop elaboration all prevent us from - // dropping `ptr_temp.deref()`. - this.into(&ptr_temp.deref(), None, block, val) + let block = unpack!(this.into(&ptr_temp, block, ptr)); + this.into(&ptr_temp.deref(), block, val) } else { let args: Vec<_> = args .into_iter() @@ -289,12 +265,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { from_hir_call, }, ); - schedule_drop(this); success.unit() } } ExprKind::Use { source } => { - this.into(destination, scope, block, source) + this.into(destination, block, source) } // These cases don't actually need a destination @@ -321,7 +296,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); this.cfg .push_assign(block, source_info, destination, rvalue); - schedule_drop(this); block.unit() } ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => { @@ -341,7 +315,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place)); this.cfg .push_assign(block, source_info, destination, rvalue); - schedule_drop(this); block.unit() } @@ -373,7 +346,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let rvalue = unpack!(block = this.as_local_rvalue(block, expr)); this.cfg.push_assign(block, source_info, destination, rvalue); - schedule_drop(this); block.unit() } }; diff --git a/src/librustc_mir/build/into.rs b/src/librustc_mir/build/into.rs index e57f10f0b14e9..077840c9ccf17 100644 --- a/src/librustc_mir/build/into.rs +++ b/src/librustc_mir/build/into.rs @@ -6,7 +6,6 @@ use crate::build::{BlockAnd, Builder}; use crate::hair::*; -use rustc::middle::region; use rustc::mir::*; pub(in crate::build) trait EvalInto<'tcx> { @@ -14,23 +13,19 @@ pub(in crate::build) trait EvalInto<'tcx> { self, builder: &mut Builder<'_, 'tcx>, destination: &Place<'tcx>, - scope: Option, block: BasicBlock, ) -> BlockAnd<()>; } impl<'a, 'tcx> Builder<'a, 'tcx> { - pub fn into( - &mut self, - destination: &Place<'tcx>, - scope: Option, - block: BasicBlock, - expr: E, - ) -> BlockAnd<()> - where - E: EvalInto<'tcx>, + pub fn into(&mut self, + destination: &Place<'tcx>, + block: BasicBlock, + expr: E) + -> BlockAnd<()> + where E: EvalInto<'tcx> { - expr.eval_into(self, destination, scope, block) + expr.eval_into(self, destination, block) } } @@ -39,11 +34,10 @@ impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> { self, builder: &mut Builder<'_, 'tcx>, destination: &Place<'tcx>, - scope: Option, block: BasicBlock, ) -> BlockAnd<()> { let expr = builder.hir.mirror(self); - builder.into_expr(destination, scope, block, expr) + builder.into_expr(destination, block, expr) } } @@ -52,9 +46,8 @@ impl<'tcx> EvalInto<'tcx> for Expr<'tcx> { self, builder: &mut Builder<'_, 'tcx>, destination: &Place<'tcx>, - scope: Option, block: BasicBlock, ) -> BlockAnd<()> { - builder.into_expr(destination, scope, block, self) + builder.into_expr(destination, block, self) } } diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index e8cf827aa900b..2e451fc88d95c 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -102,7 +102,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub fn match_expr( &mut self, destination: &Place<'tcx>, - destination_scope: Option, span: Span, mut block: BasicBlock, scrutinee: ExprRef<'tcx>, @@ -229,14 +228,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }; // Step 5. Create everything else: the guards and the arms. - if let Some(scope) = destination_scope { - // `match` assigns to its destination in each arm. Since we can't - // easily unschedule drops, we schedule the drop now. - let local = destination.as_local() - .expect("cannot schedule drop of non-Local place"); - self.schedule_drop(span, scope, local, DropKind::Value); - } - let match_scope = self.scopes.topmost(); let arm_end_blocks: Vec<_> = arm_candidates.into_iter().map(|(arm, mut candidates)| { @@ -284,8 +275,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.source_scope = source_scope; } - // No scope is provided, since we've scheduled the drop above. - this.into(destination, None, arm_block, body) + this.into(destination, arm_block, body) }) }).collect(); @@ -321,9 +311,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } => { let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); - let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); + unpack!(block = self.into(&place, block, initializer)); - unpack!(block = self.into(&place, Some(region_scope), block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let source_info = self.source_info(irrefutable_pat.span); @@ -335,6 +324,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ); + self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); block.unit() } @@ -362,10 +352,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { user_ty_span, }, } => { - let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); let place = self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); - unpack!(block = self.into(&place, Some(region_scope), block, initializer)); + unpack!(block = self.into(&place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. let pattern_source_info = self.source_info(irrefutable_pat.span); @@ -411,6 +400,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ); + self.schedule_drop_for_binding(var, irrefutable_pat.span, OutsideGuard); block.unit() } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 9a98cf1cb88c1..8c35342d324b7 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -616,7 +616,6 @@ where let source_info = builder.source_info(span); let call_site_s = (call_site_scope, source_info); unpack!(block = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| { - builder.schedule_drop(span, call_site_scope, RETURN_PLACE, DropKind::Value); if should_abort_on_panic(tcx, fn_def_id, abi) { builder.schedule_abort(); } @@ -647,7 +646,6 @@ where builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); } - builder.unschedule_return_place_drop(); return_block.unit() })); assert_eq!(block, builder.return_block()); @@ -689,9 +687,7 @@ fn construct_const<'a, 'tcx>( let mut block = START_BLOCK; let ast_expr = &tcx.hir().body(body_id).value; let expr = builder.hir.mirror(ast_expr); - // We don't provide a scope because we can't unwind in constants, so won't - // need to drop the return place. - unpack!(block = builder.into_expr(&Place::return_place(), None, block, expr)); + unpack!(block = builder.into_expr(&Place::return_place(), block, expr)); let source_info = builder.source_info(span); builder.cfg.terminate(block, source_info, TerminatorKind::Return); @@ -892,9 +888,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } let body = self.hir.mirror(ast_body); - // No scope is provided, since we've scheduled the drop of the return - // place. - self.into(&Place::return_place(), None, block, body) + self.into(&Place::return_place(), block, body) } fn set_correct_source_scope_for_arg( diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 6206bfd9f3eac..a749b4263ea64 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -513,7 +513,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let Some(value) = value { debug!("stmt_expr Break val block_context.push(SubExpr)"); self.block_context.push(BlockFrame::SubExpr); - unpack!(block = self.into(&destination, None, block, value)); + unpack!(block = self.into(&destination, block, value)); self.block_context.pop(); } else { self.cfg.push_assign_unit(block, source_info, &destination) @@ -1070,18 +1070,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block } - /// Unschedules the drop of the return place. - /// - /// If the return type of a function requires drop, then we schedule it - /// in the outermost scope so that it's dropped if there's a panic while - /// we drop any local variables. But we don't want to drop it if we - /// return normally. - crate fn unschedule_return_place_drop(&mut self) { - assert_eq!(self.scopes.len(), 1); - assert!(self.scopes.scopes[0].drops.len() <= 1); - self.scopes.scopes[0].drops.clear(); - } - // `match` arm scopes // ================== /// Unschedules any drops in the top scope. diff --git a/src/test/mir-opt/box_expr.rs b/src/test/mir-opt/box_expr.rs index 76098731947fe..8dc6b73edf6d4 100644 --- a/src/test/mir-opt/box_expr.rs +++ b/src/test/mir-opt/box_expr.rs @@ -41,36 +41,33 @@ impl Drop for S { // // bb2: { // _1 = move _2; -// drop(_2) -> [return: bb5, unwind: bb4]; +// drop(_2) -> bb4; // } // // bb3 (cleanup): { // drop(_2) -> bb1; // } // -// bb4 (cleanup): { -// drop(_1) -> bb1; -// } -// -// bb5: { +// bb4: { // StorageDead(_2); // StorageLive(_3); // StorageLive(_4); // _4 = move _1; -// _3 = const std::mem::drop::>(move _4) -> [return: bb6, unwind: bb7]; +// _3 = const std::mem::drop::>(move _4) -> [return: bb5, unwind: bb7]; // } // -// bb6: { +// bb5: { // StorageDead(_4); // StorageDead(_3); // _0 = (); // drop(_1) -> bb8; // } -// +// bb6 (cleanup): { +// drop(_1) -> bb1; +// } // bb7 (cleanup): { -// drop(_4) -> bb4; +// drop(_4) -> bb6; // } -// // bb8: { // StorageDead(_1); // return; diff --git a/src/test/mir-opt/issue-62289.rs b/src/test/mir-opt/issue-62289.rs index e8dd56cbbae22..a3b517e9bca87 100644 --- a/src/test/mir-opt/issue-62289.rs +++ b/src/test/mir-opt/issue-62289.rs @@ -24,7 +24,7 @@ fn main() { // StorageLive(_3); // StorageLive(_4); // _4 = std::option::Option::::None; -// _3 = const as std::ops::Try>::into_result(move _4) -> [return: bb2, unwind: bb4]; +// _3 = const as std::ops::Try>::into_result(move _4) -> [return: bb2, unwind: bb3]; // } // bb1 (cleanup): { // resume; @@ -32,63 +32,60 @@ fn main() { // bb2: { // StorageDead(_4); // _5 = discriminant(_3); -// switchInt(move _5) -> [0isize: bb11, 1isize: bb6, otherwise: bb5]; +// switchInt(move _5) -> [0isize: bb10, 1isize: bb5, otherwise: bb4]; // } // bb3 (cleanup): { -// drop(_0) -> bb1; +// drop(_2) -> bb1; // } -// bb4 (cleanup): { -// drop(_2) -> bb3; -// } -// bb5: { +// bb4: { // unreachable; // } -// bb6: { +// bb5: { // StorageLive(_6); // _6 = ((_3 as Err).0: std::option::NoneError); // StorageLive(_8); // StorageLive(_9); // _9 = _6; -// _8 = const >::from(move _9) -> [return: bb8, unwind: bb4]; +// _8 = const >::from(move _9) -> [return: bb7, unwind: bb3]; // } -// bb7: { +// bb6: { // return; // } -// bb8: { +// bb7: { // StorageDead(_9); -// _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb9, unwind: bb4]; +// _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb8, unwind: bb3]; // } -// bb9: { +// bb8: { // StorageDead(_8); // StorageDead(_6); -// drop(_2) -> [return: bb10, unwind: bb3]; +// drop(_2) -> bb9; // } -// bb10: { +// bb9: { // StorageDead(_2); // StorageDead(_1); // StorageDead(_3); -// goto -> bb7; +// goto -> bb6; // } -// bb11: { +// bb10: { // StorageLive(_10); // _10 = ((_3 as Ok).0: u32); // (*_2) = _10; // StorageDead(_10); // _1 = move _2; -// drop(_2) -> [return: bb13, unwind: bb12]; +// drop(_2) -> [return: bb12, unwind: bb11]; // } -// bb12 (cleanup): { -// drop(_1) -> bb3; +// bb11 (cleanup): { +// drop(_1) -> bb1; // } -// bb13: { +// bb12: { // StorageDead(_2); // _0 = std::option::Option::>::Some(move _1,); -// drop(_1) -> [return: bb14, unwind: bb3]; +// drop(_1) -> bb13; // } -// bb14: { +// bb13: { // StorageDead(_1); // StorageDead(_3); -// goto -> bb7; +// goto -> bb6; // } // } // END rustc.test.ElaborateDrops.before.mir diff --git a/src/test/ui/async-await/async-fn-size-uninit-locals.rs b/src/test/ui/async-await/async-fn-size-uninit-locals.rs index a489fb11630cd..ad20237981c30 100644 --- a/src/test/ui/async-await/async-fn-size-uninit-locals.rs +++ b/src/test/ui/async-await/async-fn-size-uninit-locals.rs @@ -99,5 +99,5 @@ fn main() { assert_eq!(12, std::mem::size_of_val(&single_with_noop())); assert_eq!(3084, std::mem::size_of_val(&joined())); assert_eq!(3084, std::mem::size_of_val(&joined_with_noop())); - assert_eq!(3084, std::mem::size_of_val(&join_retval())); + assert_eq!(3080, std::mem::size_of_val(&join_retval())); } diff --git a/src/test/ui/drop/dynamic-drop-async.rs b/src/test/ui/drop/dynamic-drop-async.rs index 398bcb7ec0e82..91063edf0f6c4 100644 --- a/src/test/ui/drop/dynamic-drop-async.rs +++ b/src/test/ui/drop/dynamic-drop-async.rs @@ -7,7 +7,7 @@ // edition:2018 // ignore-wasm32-bare compiled with panic=abort by default -#![feature(slice_patterns, arbitrary_self_types)] +#![feature(slice_patterns)] #![allow(unused)] use std::{ @@ -45,7 +45,6 @@ impl Future for Defer { /// The `failing_op`-th operation will panic. struct Allocator { data: RefCell>, - name: &'static str, failing_op: usize, cur_ops: Cell, } @@ -57,28 +56,23 @@ impl Drop for Allocator { fn drop(&mut self) { let data = self.data.borrow(); if data.iter().any(|d| *d) { - panic!("missing free in {:?}: {:?}", self.name, data); + panic!("missing free: {:?}", data); } } } impl Allocator { - fn new(failing_op: usize, name: &'static str) -> Self { - Allocator { - failing_op, - name, - cur_ops: Cell::new(0), - data: RefCell::new(vec![]), - } + fn new(failing_op: usize) -> Self { + Allocator { failing_op, cur_ops: Cell::new(0), data: RefCell::new(vec![]) } } - fn alloc(self: &Rc) -> impl Future + 'static { + fn alloc(&self) -> impl Future> + '_ { self.fallible_operation(); let mut data = self.data.borrow_mut(); let addr = data.len(); data.push(true); - Defer { ready: false, value: Some(Ptr(addr, self.clone())) } + Defer { ready: false, value: Some(Ptr(addr, self)) } } fn fallible_operation(&self) { self.cur_ops.set(self.cur_ops.get() + 1); @@ -91,11 +85,11 @@ impl Allocator { // Type that tracks whether it was dropped and can panic when it's created or // destroyed. -struct Ptr(usize, Rc); -impl Drop for Ptr { +struct Ptr<'a>(usize, &'a Allocator); +impl<'a> Drop for Ptr<'a> { fn drop(&mut self) { match self.1.data.borrow_mut()[self.0] { - false => panic!("double free in {:?} at index {:?}", self.1.name, self.0), + false => panic!("double free at index {:?}", self.0), ref mut d => *d = false, } @@ -119,7 +113,7 @@ async fn dynamic_drop(a: Rc, c: bool) { }; } -struct TwoPtrs(Ptr, Ptr); +struct TwoPtrs<'a>(Ptr<'a>, Ptr<'a>); async fn struct_dynamic_drop(a: Rc, c0: bool, c1: bool, c: bool) { for i in 0..2 { let x; @@ -234,62 +228,21 @@ async fn subslice_pattern_reassign(a: Rc) { a.alloc().await; } -async fn panic_after_return(a: Rc, c: bool) -> (Ptr,) { - a.alloc().await; - let p = a.alloc().await; - if c { - a.alloc().await; - let q = a.alloc().await; - // We use a return type that isn't used anywhere else to make sure that - // the return place doesn't incorrectly end up in the generator state. - return (a.alloc().await,); - } - (a.alloc().await,) -} - - -async fn panic_after_init_by_loop(a: Rc) { - a.alloc().await; - let p = a.alloc().await; - let q = loop { - a.alloc().await; - let r = a.alloc().await; - break a.alloc().await; - }; -} - -async fn panic_after_init_by_match_with_bindings_and_guard(a: Rc, b: bool) { - a.alloc().await; - let p = a.alloc().await; - let q = match a.alloc().await { - ref _x if b => { - a.alloc().await; - let r = a.alloc().await; - a.alloc().await - } - _x => { - a.alloc().await; - let r = a.alloc().await; - a.alloc().await - }, - }; -} - -fn run_test(cx: &mut Context<'_>, ref f: F, name: &'static str) +fn run_test(cx: &mut Context<'_>, ref f: F) where F: Fn(Rc) -> G, - G: Future, + G: Future, { for polls in 0.. { // Run without any panics to find which operations happen after the // penultimate `poll`. - let first_alloc = Rc::new(Allocator::new(usize::MAX, name)); + let first_alloc = Rc::new(Allocator::new(usize::MAX)); let mut fut = Box::pin(f(first_alloc.clone())); let mut ops_before_last_poll = 0; let mut completed = false; for _ in 0..polls { ops_before_last_poll = first_alloc.cur_ops.get(); - if let Poll::Ready(_) = fut.as_mut().poll(cx) { + if let Poll::Ready(()) = fut.as_mut().poll(cx) { completed = true; } } @@ -298,7 +251,7 @@ where // Start at `ops_before_last_poll` so that we will always be able to // `poll` the expected number of times. for failing_op in ops_before_last_poll..first_alloc.cur_ops.get() { - let alloc = Rc::new(Allocator::new(failing_op + 1, name)); + let alloc = Rc::new(Allocator::new(failing_op + 1)); let f = &f; let cx = &mut *cx; let result = panic::catch_unwind(panic::AssertUnwindSafe(move || { @@ -328,56 +281,46 @@ fn clone_waker(data: *const ()) -> RawWaker { RawWaker::new(data, &RawWakerVTable::new(clone_waker, drop, drop, drop)) } -macro_rules! run_test { - ($ctxt:expr, $e:expr) => { run_test($ctxt, $e, stringify!($e)); }; -} - fn main() { let waker = unsafe { Waker::from_raw(clone_waker(ptr::null())) }; let context = &mut Context::from_waker(&waker); - run_test!(context, |a| dynamic_init(a, false)); - run_test!(context, |a| dynamic_init(a, true)); - run_test!(context, |a| dynamic_drop(a, false)); - run_test!(context, |a| dynamic_drop(a, true)); - - run_test!(context, |a| assignment(a, false, false)); - run_test!(context, |a| assignment(a, false, true)); - run_test!(context, |a| assignment(a, true, false)); - run_test!(context, |a| assignment(a, true, true)); - - run_test!(context, |a| array_simple(a)); - run_test!(context, |a| vec_simple(a)); - run_test!(context, |a| vec_unreachable(a)); - - run_test!(context, |a| struct_dynamic_drop(a, false, false, false)); - run_test!(context, |a| struct_dynamic_drop(a, false, false, true)); - run_test!(context, |a| struct_dynamic_drop(a, false, true, false)); - run_test!(context, |a| struct_dynamic_drop(a, false, true, true)); - run_test!(context, |a| struct_dynamic_drop(a, true, false, false)); - run_test!(context, |a| struct_dynamic_drop(a, true, false, true)); - run_test!(context, |a| struct_dynamic_drop(a, true, true, false)); - run_test!(context, |a| struct_dynamic_drop(a, true, true, true)); - - run_test!(context, |a| field_assignment(a, false)); - run_test!(context, |a| field_assignment(a, true)); - - run_test!(context, |a| mixed_drop_and_nondrop(a)); - - run_test!(context, |a| slice_pattern_one_of(a, 0)); - run_test!(context, |a| slice_pattern_one_of(a, 1)); - run_test!(context, |a| slice_pattern_one_of(a, 2)); - run_test!(context, |a| slice_pattern_one_of(a, 3)); - - run_test!(context, |a| subslice_pattern_from_end_with_drop(a, true, true)); - run_test!(context, |a| subslice_pattern_from_end_with_drop(a, true, false)); - run_test!(context, |a| subslice_pattern_from_end_with_drop(a, false, true)); - run_test!(context, |a| subslice_pattern_from_end_with_drop(a, false, false)); - run_test!(context, |a| subslice_pattern_reassign(a)); - - run_test!(context, |a| panic_after_return(a, false)); - run_test!(context, |a| panic_after_return(a, true)); - run_test!(context, |a| panic_after_init_by_loop(a)); - run_test!(context, |a| panic_after_init_by_match_with_bindings_and_guard(a, false)); - run_test!(context, |a| panic_after_init_by_match_with_bindings_and_guard(a, true)); + run_test(context, |a| dynamic_init(a, false)); + run_test(context, |a| dynamic_init(a, true)); + run_test(context, |a| dynamic_drop(a, false)); + run_test(context, |a| dynamic_drop(a, true)); + + run_test(context, |a| assignment(a, false, false)); + run_test(context, |a| assignment(a, false, true)); + run_test(context, |a| assignment(a, true, false)); + run_test(context, |a| assignment(a, true, true)); + + run_test(context, |a| array_simple(a)); + run_test(context, |a| vec_simple(a)); + run_test(context, |a| vec_unreachable(a)); + + run_test(context, |a| struct_dynamic_drop(a, false, false, false)); + run_test(context, |a| struct_dynamic_drop(a, false, false, true)); + run_test(context, |a| struct_dynamic_drop(a, false, true, false)); + run_test(context, |a| struct_dynamic_drop(a, false, true, true)); + run_test(context, |a| struct_dynamic_drop(a, true, false, false)); + run_test(context, |a| struct_dynamic_drop(a, true, false, true)); + run_test(context, |a| struct_dynamic_drop(a, true, true, false)); + run_test(context, |a| struct_dynamic_drop(a, true, true, true)); + + run_test(context, |a| field_assignment(a, false)); + run_test(context, |a| field_assignment(a, true)); + + run_test(context, |a| mixed_drop_and_nondrop(a)); + + run_test(context, |a| slice_pattern_one_of(a, 0)); + run_test(context, |a| slice_pattern_one_of(a, 1)); + run_test(context, |a| slice_pattern_one_of(a, 2)); + run_test(context, |a| slice_pattern_one_of(a, 3)); + + run_test(context, |a| subslice_pattern_from_end_with_drop(a, true, true)); + run_test(context, |a| subslice_pattern_from_end_with_drop(a, true, false)); + run_test(context, |a| subslice_pattern_from_end_with_drop(a, false, true)); + run_test(context, |a| subslice_pattern_from_end_with_drop(a, false, false)); + run_test(context, |a| subslice_pattern_reassign(a)); } diff --git a/src/test/ui/drop/dynamic-drop.rs b/src/test/ui/drop/dynamic-drop.rs index 6f9112ae00605..8516bc3d96424 100644 --- a/src/test/ui/drop/dynamic-drop.rs +++ b/src/test/ui/drop/dynamic-drop.rs @@ -17,7 +17,6 @@ struct InjectedFailure; struct Allocator { data: RefCell>, - name: &'static str, failing_op: usize, cur_ops: Cell, } @@ -29,18 +28,17 @@ impl Drop for Allocator { fn drop(&mut self) { let data = self.data.borrow(); if data.iter().any(|d| *d) { - panic!("missing free in {:?}: {:?}", self.name, data); + panic!("missing free: {:?}", data); } } } impl Allocator { - fn new(failing_op: usize, name: &'static str) -> Self { + fn new(failing_op: usize) -> Self { Allocator { failing_op: failing_op, cur_ops: Cell::new(0), - data: RefCell::new(vec![]), - name, + data: RefCell::new(vec![]) } } fn alloc(&self) -> Ptr<'_> { @@ -55,6 +53,20 @@ impl Allocator { data.push(true); Ptr(addr, self) } + // FIXME(#47949) Any use of this indicates a bug in rustc: we should never + // be leaking values in the cases here. + // + // Creates a `Ptr<'_>` and checks that the allocated value is leaked if the + // `failing_op` is in the list of exception. + fn alloc_leaked(&self, exceptions: Vec) -> Ptr<'_> { + let ptr = self.alloc(); + + if exceptions.iter().any(|operation| *operation == self.failing_op) { + let mut data = self.data.borrow_mut(); + data[ptr.0] = false; + } + ptr + } } struct Ptr<'a>(usize, &'a Allocator); @@ -62,7 +74,7 @@ impl<'a> Drop for Ptr<'a> { fn drop(&mut self) { match self.1.data.borrow_mut()[self.0] { false => { - panic!("double free in {:?} at index {:?}", self.1.name, self.0) + panic!("double free at index {:?}", self.0) } ref mut d => *d = false } @@ -258,148 +270,79 @@ fn subslice_pattern_reassign(a: &Allocator) { } fn panic_after_return(a: &Allocator) -> Ptr<'_> { + // Panic in the drop of `p` or `q` can leak + let exceptions = vec![8, 9]; a.alloc(); let p = a.alloc(); { a.alloc(); let p = a.alloc(); - a.alloc() + // FIXME (#47949) We leak values when we panic in a destructor after + // evaluating an expression with `rustc_mir::build::Builder::into`. + a.alloc_leaked(exceptions) } } fn panic_after_return_expr(a: &Allocator) -> Ptr<'_> { + // Panic in the drop of `p` or `q` can leak + let exceptions = vec![8, 9]; a.alloc(); let p = a.alloc(); { a.alloc(); let q = a.alloc(); - return a.alloc(); + // FIXME (#47949) + return a.alloc_leaked(exceptions); } } fn panic_after_init(a: &Allocator) { + // Panic in the drop of `r` can leak + let exceptions = vec![8]; a.alloc(); let p = a.alloc(); let q = { a.alloc(); let r = a.alloc(); - a.alloc() + // FIXME (#47949) + a.alloc_leaked(exceptions) }; } fn panic_after_init_temp(a: &Allocator) { + // Panic in the drop of `r` can leak + let exceptions = vec![8]; a.alloc(); let p = a.alloc(); { a.alloc(); let r = a.alloc(); - a.alloc() + // FIXME (#47949) + a.alloc_leaked(exceptions) }; } fn panic_after_init_by_loop(a: &Allocator) { + // Panic in the drop of `r` can leak + let exceptions = vec![8]; a.alloc(); let p = a.alloc(); let q = loop { a.alloc(); let r = a.alloc(); - break a.alloc(); - }; -} - -fn panic_after_init_by_match(a: &Allocator, b: bool) { - a.alloc(); - let p = a.alloc(); - loop { - let q = match b { - true => { - a.alloc(); - let r = a.alloc(); - a.alloc() - } - false => { - a.alloc(); - let r = a.alloc(); - break a.alloc(); - } - }; - return; - }; -} - -fn panic_after_init_by_match_with_guard(a: &Allocator, b: bool) { - a.alloc(); - let p = a.alloc(); - let q = match a.alloc() { - _ if b => { - a.alloc(); - let r = a.alloc(); - a.alloc() - } - _ => { - a.alloc(); - let r = a.alloc(); - a.alloc() - }, - }; -} - -fn panic_after_init_by_match_with_bindings_and_guard(a: &Allocator, b: bool) { - a.alloc(); - let p = a.alloc(); - let q = match a.alloc() { - _x if b => { - a.alloc(); - let r = a.alloc(); - a.alloc() - } - _x => { - a.alloc(); - let r = a.alloc(); - a.alloc() - }, - }; -} - -fn panic_after_init_by_match_with_ref_bindings_and_guard(a: &Allocator, b: bool) { - a.alloc(); - let p = a.alloc(); - let q = match a.alloc() { - ref _x if b => { - a.alloc(); - let r = a.alloc(); - a.alloc() - } - ref _x => { - a.alloc(); - let r = a.alloc(); - a.alloc() - }, - }; -} - -fn panic_after_init_by_break_if(a: &Allocator, b: bool) { - a.alloc(); - let p = a.alloc(); - let q = loop { - let r = a.alloc(); - break if b { - let s = a.alloc(); - a.alloc() - } else { - a.alloc() - }; + // FIXME (#47949) + break a.alloc_leaked(exceptions); }; } -fn run_test(mut f: F, name: &'static str) +fn run_test(mut f: F) where F: FnMut(&Allocator) { - let first_alloc = Allocator::new(usize::MAX, name); + let first_alloc = Allocator::new(usize::MAX); f(&first_alloc); for failing_op in 1..first_alloc.cur_ops.get()+1 { - let alloc = Allocator::new(failing_op, name); + let alloc = Allocator::new(failing_op); let alloc = &alloc; let f = panic::AssertUnwindSafe(&mut f); let result = panic::catch_unwind(move || { @@ -417,91 +360,77 @@ fn run_test(mut f: F, name: &'static str) } } -fn run_test_nopanic(mut f: F, name: &'static str) +fn run_test_nopanic(mut f: F) where F: FnMut(&Allocator) { - let first_alloc = Allocator::new(usize::MAX, name); + let first_alloc = Allocator::new(usize::MAX); f(&first_alloc); } -macro_rules! run_test { - ($e:expr) => { run_test($e, stringify!($e)); } -} - fn main() { - run_test!(|a| dynamic_init(a, false)); - run_test!(|a| dynamic_init(a, true)); - run_test!(|a| dynamic_drop(a, false)); - run_test!(|a| dynamic_drop(a, true)); - - run_test!(|a| assignment2(a, false, false)); - run_test!(|a| assignment2(a, false, true)); - run_test!(|a| assignment2(a, true, false)); - run_test!(|a| assignment2(a, true, true)); - - run_test!(|a| assignment1(a, false)); - run_test!(|a| assignment1(a, true)); - - run_test!(|a| array_simple(a)); - run_test!(|a| vec_simple(a)); - run_test!(|a| vec_unreachable(a)); - - run_test!(|a| struct_dynamic_drop(a, false, false, false)); - run_test!(|a| struct_dynamic_drop(a, false, false, true)); - run_test!(|a| struct_dynamic_drop(a, false, true, false)); - run_test!(|a| struct_dynamic_drop(a, false, true, true)); - run_test!(|a| struct_dynamic_drop(a, true, false, false)); - run_test!(|a| struct_dynamic_drop(a, true, false, true)); - run_test!(|a| struct_dynamic_drop(a, true, true, false)); - run_test!(|a| struct_dynamic_drop(a, true, true, true)); - - run_test!(|a| field_assignment(a, false)); - run_test!(|a| field_assignment(a, true)); - - run_test!(|a| generator(a, 0)); - run_test!(|a| generator(a, 1)); - run_test!(|a| generator(a, 2)); - run_test!(|a| generator(a, 3)); - - run_test!(|a| mixed_drop_and_nondrop(a)); - - run_test!(|a| slice_pattern_first(a)); - run_test!(|a| slice_pattern_middle(a)); - run_test!(|a| slice_pattern_two(a)); - run_test!(|a| slice_pattern_last(a)); - run_test!(|a| slice_pattern_one_of(a, 0)); - run_test!(|a| slice_pattern_one_of(a, 1)); - run_test!(|a| slice_pattern_one_of(a, 2)); - run_test!(|a| slice_pattern_one_of(a, 3)); - - run_test!(|a| subslice_pattern_from_end(a, true)); - run_test!(|a| subslice_pattern_from_end(a, false)); - run_test!(|a| subslice_pattern_from_end_with_drop(a, true, true)); - run_test!(|a| subslice_pattern_from_end_with_drop(a, true, false)); - run_test!(|a| subslice_pattern_from_end_with_drop(a, false, true)); - run_test!(|a| subslice_pattern_from_end_with_drop(a, false, false)); - run_test!(|a| slice_pattern_reassign(a)); - run_test!(|a| subslice_pattern_reassign(a)); - - run_test!(|a| { + run_test(|a| dynamic_init(a, false)); + run_test(|a| dynamic_init(a, true)); + run_test(|a| dynamic_drop(a, false)); + run_test(|a| dynamic_drop(a, true)); + + run_test(|a| assignment2(a, false, false)); + run_test(|a| assignment2(a, false, true)); + run_test(|a| assignment2(a, true, false)); + run_test(|a| assignment2(a, true, true)); + + run_test(|a| assignment1(a, false)); + run_test(|a| assignment1(a, true)); + + run_test(|a| array_simple(a)); + run_test(|a| vec_simple(a)); + run_test(|a| vec_unreachable(a)); + + run_test(|a| struct_dynamic_drop(a, false, false, false)); + run_test(|a| struct_dynamic_drop(a, false, false, true)); + run_test(|a| struct_dynamic_drop(a, false, true, false)); + run_test(|a| struct_dynamic_drop(a, false, true, true)); + run_test(|a| struct_dynamic_drop(a, true, false, false)); + run_test(|a| struct_dynamic_drop(a, true, false, true)); + run_test(|a| struct_dynamic_drop(a, true, true, false)); + run_test(|a| struct_dynamic_drop(a, true, true, true)); + + run_test(|a| field_assignment(a, false)); + run_test(|a| field_assignment(a, true)); + + run_test(|a| generator(a, 0)); + run_test(|a| generator(a, 1)); + run_test(|a| generator(a, 2)); + run_test(|a| generator(a, 3)); + + run_test(|a| mixed_drop_and_nondrop(a)); + + run_test(|a| slice_pattern_first(a)); + run_test(|a| slice_pattern_middle(a)); + run_test(|a| slice_pattern_two(a)); + run_test(|a| slice_pattern_last(a)); + run_test(|a| slice_pattern_one_of(a, 0)); + run_test(|a| slice_pattern_one_of(a, 1)); + run_test(|a| slice_pattern_one_of(a, 2)); + run_test(|a| slice_pattern_one_of(a, 3)); + + run_test(|a| subslice_pattern_from_end(a, true)); + run_test(|a| subslice_pattern_from_end(a, false)); + run_test(|a| subslice_pattern_from_end_with_drop(a, true, true)); + run_test(|a| subslice_pattern_from_end_with_drop(a, true, false)); + run_test(|a| subslice_pattern_from_end_with_drop(a, false, true)); + run_test(|a| subslice_pattern_from_end_with_drop(a, false, false)); + run_test(|a| slice_pattern_reassign(a)); + run_test(|a| subslice_pattern_reassign(a)); + + run_test(|a| { panic_after_return(a); }); - run_test!(|a| { + run_test(|a| { panic_after_return_expr(a); }); - run_test!(|a| panic_after_init(a)); - run_test!(|a| panic_after_init_temp(a)); - run_test!(|a| panic_after_init_by_loop(a)); - run_test!(|a| panic_after_init_by_match(a, false)); - run_test!(|a| panic_after_init_by_match(a, true)); - run_test!(|a| panic_after_init_by_match_with_guard(a, false)); - run_test!(|a| panic_after_init_by_match_with_guard(a, true)); - run_test!(|a| panic_after_init_by_match_with_bindings_and_guard(a, false)); - run_test!(|a| panic_after_init_by_match_with_bindings_and_guard(a, true)); - run_test!(|a| panic_after_init_by_match_with_ref_bindings_and_guard(a, false)); - run_test!(|a| panic_after_init_by_match_with_ref_bindings_and_guard(a, true)); - run_test!(|a| panic_after_init_by_break_if(a, false)); - run_test!(|a| panic_after_init_by_break_if(a, true)); - - run_test_nopanic(|a| union1(a), "|a| union1(a)"); + run_test(|a| panic_after_init(a)); + run_test(|a| panic_after_init_temp(a)); + run_test(|a| panic_after_init_by_loop(a)); + + run_test_nopanic(|a| union1(a)); }