Skip to content

Commit

Permalink
Auto merge of rust-lang#61430 - matthewjasper:drop-on-into-panic, r=o…
Browse files Browse the repository at this point in the history
…li-obk

Make `into` schedule drop for the destination

closes rust-lang#47949
  • Loading branch information
bors committed Oct 7, 2019
2 parents 59a31c8 + 3702683 commit f3c9cec
Show file tree
Hide file tree
Showing 17 changed files with 621 additions and 286 deletions.
70 changes: 49 additions & 21 deletions src/librustc_mir/build/block.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
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>,
block: BasicBlock,
ast_block: &'tcx hir::Block,
source_info: SourceInfo)
-> BlockAnd<()> {
pub fn ast_block(
&mut self,
destination: &Place<'tcx>,
scope: Option<region::Scope>,
block: BasicBlock,
ast_block: &'tcx hir::Block,
source_info: SourceInfo,
) -> BlockAnd<()> {
let Block {
region_scope,
opt_destruction_scope,
Expand All @@ -21,37 +25,61 @@ 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, block, span, stmts, expr,
safety_mode)
this.ast_block_stmts(
destination,
None,
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, block, span, stmts, expr,
safety_mode)
this.ast_block_stmts(
destination,
scope,
block,
span,
stmts,
expr,
safety_mode,
)
}
})
})
}

fn ast_block_stmts(&mut self,
destination: &Place<'tcx>,
mut block: BasicBlock,
span: Span,
stmts: Vec<StmtRef<'tcx>>,
expr: Option<ExprRef<'tcx>>,
safety_mode: BlockSafety)
-> BlockAnd<()> {
fn ast_block_stmts(
&mut self,
destination: &Place<'tcx>,
scope: Option<region::Scope>,
mut block: BasicBlock,
span: Span,
stmts: Vec<StmtRef<'tcx>>,
expr: Option<ExprRef<'tcx>>,
safety_mode: BlockSafety,
) -> BlockAnd<()> {
let this = self;

// This convoluted structure is to avoid using recursion as we walk down a list
Expand Down Expand Up @@ -177,7 +205,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, block, expr));
unpack!(block = this.into(destination, scope, block, expr));
let popped = this.block_context.pop();

assert!(popped.map_or(false, |bf|bf.is_tail_expr()));
Expand Down
9 changes: 5 additions & 4 deletions src/librustc_mir/build/expr/as_rvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr_span,
scope,
result,
expr.ty,
);
}

Expand All @@ -137,11 +136,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.cfg
.push_assign(block, source_info, &Place::from(result), box_);

// initialize the box contents:
// Initialize the box contents. No scope is needed since the
// `Box` is already scheduled to be dropped.
unpack!(
block = this.into(
&Place::from(result).deref(),
block, value
None,
block,
value
)
);
block.and(Rvalue::Use(Operand::Move(Place::from(result))))
Expand Down Expand Up @@ -569,7 +571,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
upvar_span,
temp_lifetime,
temp,
upvar_ty,
);
}

Expand Down
13 changes: 1 addition & 12 deletions src/librustc_mir/build/expr/as_temp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,24 +103,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr_span,
temp_lifetime,
temp,
expr_ty,
DropKind::Storage,
);
}
}
}

unpack!(block = this.into(temp_place, block, expr));

if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop(
expr_span,
temp_lifetime,
temp,
expr_ty,
DropKind::Value,
);
}
unpack!(block = this.into(temp_place, temp_lifetime, block, expr));

block.and(temp)
}
Expand Down
46 changes: 37 additions & 9 deletions src/librustc_mir/build/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
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;

Expand All @@ -11,15 +13,18 @@ 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<region::Scope>,
mut block: BasicBlock,
expr: Expr<'tcx>,
) -> BlockAnd<()> {
debug!(
"into_expr(destination={:?}, block={:?}, expr={:?})",
destination, block, expr
"into_expr(destination={:?}, scope={:?}, block={:?}, expr={:?})",
destination, scope, block, expr
);

// since we frequently have to reference `self` from within a
Expand All @@ -35,6 +40,14 @@ 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);
}
Expand All @@ -47,14 +60,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, block, value)
this.into(destination, scope, block, value)
})
}
ExprKind::Block { body: ast_block } => {
this.ast_block(destination, block, ast_block, source_info)
this.ast_block(destination, scope, block, ast_block, source_info)
}
ExprKind::Match { scrutinee, arms } => {
this.match_expr(destination, expr_span, block, scrutinee, arms)
this.match_expr(destination, scope, expr_span, block, scrutinee, arms)
}
ExprKind::NeverToAny { source } => {
let source = this.hir.mirror(source);
Expand All @@ -67,6 +80,7 @@ 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 {
Expand Down Expand Up @@ -164,6 +178,9 @@ 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,
Expand All @@ -185,7 +202,8 @@ 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.
let body_block_end = unpack!(this.into(&tmp, body_block, body));
// No scope is provided, since we've scheduled the drop above.
let body_block_end = unpack!(this.into(&tmp, None, body_block, body));
this.cfg.terminate(
body_block_end,
source_info,
Expand Down Expand Up @@ -234,8 +252,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
is_block_tail: None,
});
let ptr_temp = Place::from(ptr_temp);
let block = unpack!(this.into(&ptr_temp, block, ptr));
this.into(&ptr_temp.deref(), block, val)
// 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)
} else {
let args: Vec<_> = args
.into_iter()
Expand Down Expand Up @@ -265,11 +289,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
from_hir_call,
},
);
schedule_drop(this);
success.unit()
}
}
ExprKind::Use { source } => {
this.into(destination, block, source)
this.into(destination, scope, block, source)
}

// These cases don't actually need a destination
Expand All @@ -296,6 +321,7 @@ 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 { .. } => {
Expand All @@ -315,6 +341,7 @@ 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()
}

Expand Down Expand Up @@ -346,6 +373,7 @@ 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()
}
};
Expand Down
25 changes: 16 additions & 9 deletions src/librustc_mir/build/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,31 @@
use crate::build::{BlockAnd, Builder};
use crate::hair::*;
use rustc::middle::region;
use rustc::mir::*;

pub(in crate::build) trait EvalInto<'tcx> {
fn eval_into(
self,
builder: &mut Builder<'_, 'tcx>,
destination: &Place<'tcx>,
scope: Option<region::Scope>,
block: BasicBlock,
) -> BlockAnd<()>;
}

impl<'a, 'tcx> Builder<'a, 'tcx> {
pub fn into<E>(&mut self,
destination: &Place<'tcx>,
block: BasicBlock,
expr: E)
-> BlockAnd<()>
where E: EvalInto<'tcx>
pub fn into<E>(
&mut self,
destination: &Place<'tcx>,
scope: Option<region::Scope>,
block: BasicBlock,
expr: E,
) -> BlockAnd<()>
where
E: EvalInto<'tcx>,
{
expr.eval_into(self, destination, block)
expr.eval_into(self, destination, scope, block)
}
}

Expand All @@ -34,10 +39,11 @@ impl<'tcx> EvalInto<'tcx> for ExprRef<'tcx> {
self,
builder: &mut Builder<'_, 'tcx>,
destination: &Place<'tcx>,
scope: Option<region::Scope>,
block: BasicBlock,
) -> BlockAnd<()> {
let expr = builder.hir.mirror(self);
builder.into_expr(destination, block, expr)
builder.into_expr(destination, scope, block, expr)
}
}

Expand All @@ -46,8 +52,9 @@ impl<'tcx> EvalInto<'tcx> for Expr<'tcx> {
self,
builder: &mut Builder<'_, 'tcx>,
destination: &Place<'tcx>,
scope: Option<region::Scope>,
block: BasicBlock,
) -> BlockAnd<()> {
builder.into_expr(destination, block, self)
builder.into_expr(destination, scope, block, self)
}
}
Loading

0 comments on commit f3c9cec

Please sign in to comment.