提交 98265d33 编写于 作者: S Simonas Kazlauskas

Convert Drop statement into terminator

The structure of the old translator as well as MIR assumed that drop glue cannot possibly panic and
translated the drops accordingly. However, in presence of `Drop::drop` this assumption can be
trivially shown to be untrue. As such, the Rust code like the following would never print number 2:

```rust
struct Droppable(u32);
impl Drop for Droppable {
    fn drop(&mut self) {
        if self.0 == 1 { panic!("Droppable(1)") } else { println!("{}", self.0) }
    }
}
fn main() {
    let x = Droppable(2);
    let y = Droppable(1);
}
```

While the behaviour is allowed according to the language rules (we allow drops to not run), that’s
a very counter-intuitive behaviour. We fix this in MIR by allowing `Drop` to have a target to take
on divergence and connect the drops in such a way so the leftover drops are executed when some drop
unwinds.

Note, that this commit still does not implement the translator part of changes necessary for the
grand scheme of things to fully work, so the actual observed behaviour does not change yet. Coming
soon™.

See #14875.
上级 65dd5e6a
......@@ -262,6 +262,13 @@ pub enum Terminator<'tcx> {
/// `END_BLOCK`.
Return,
/// Drop the Lvalue
Drop {
value: Lvalue<'tcx>,
target: BasicBlock,
unwind: Option<BasicBlock>
},
/// Block ends with a call of a converging function
Call {
/// The function that’s being called
......@@ -290,6 +297,8 @@ pub fn successors(&self) -> Cow<[BasicBlock]> {
slice::ref_slice(t).into_cow(),
Call { destination: None, cleanup: Some(ref c), .. } => slice::ref_slice(c).into_cow(),
Call { destination: None, cleanup: None, .. } => (&[]).into_cow(),
Drop { target, unwind: Some(unwind), .. } => vec![target, unwind].into_cow(),
Drop { ref target, .. } => slice::ref_slice(target).into_cow(),
}
}
......@@ -308,6 +317,8 @@ pub fn successors_mut(&mut self) -> Vec<&mut BasicBlock> {
Call { destination: Some((_, ref mut t)), cleanup: None, .. } => vec![t],
Call { destination: None, cleanup: Some(ref mut c), .. } => vec![c],
Call { destination: None, cleanup: None, .. } => vec![],
Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind],
Drop { ref mut target, .. } => vec![target]
}
}
}
......@@ -374,6 +385,7 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"),
Drop { ref value, .. } => write!(fmt, "drop({:?})", value),
Call { ref func, ref args, ref destination, .. } => {
if let Some((ref destination, _)) = *destination {
try!(write!(fmt, "{:?} = ", destination));
......@@ -418,6 +430,8 @@ pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()],
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()],
Call { destination: None, cleanup: None, .. } => vec![],
Drop { unwind: None, .. } => vec!["return".into_cow()],
Drop { .. } => vec!["return".into_cow(), "unwind".into_cow()],
}
}
}
......@@ -435,15 +449,13 @@ pub struct Statement<'tcx> {
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub enum StatementKind<'tcx> {
Assign(Lvalue<'tcx>, Rvalue<'tcx>),
Drop(Lvalue<'tcx>),
}
impl<'tcx> Debug for Statement<'tcx> {
fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
use self::StatementKind::*;
match self.kind {
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
Drop(ref lv) => write!(fmt, "drop {:?}", lv),
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv)
}
}
}
......
......@@ -124,9 +124,6 @@ fn super_statement(&mut self,
ref $($mutability)* rvalue) => {
self.visit_assign(block, lvalue, rvalue);
}
StatementKind::Drop(ref $($mutability)* lvalue) => {
self.visit_lvalue(lvalue, LvalueContext::Drop);
}
}
}
......@@ -177,10 +174,16 @@ fn super_terminator(&mut self,
Terminator::Return => {
}
Terminator::Drop { ref $($mutability)* value, target, unwind } => {
self.visit_lvalue(value, LvalueContext::Drop);
self.visit_branch(block, target);
unwind.map(|t| self.visit_branch(block, t));
}
Terminator::Call { ref $($mutability)* func,
ref $($mutability)* args,
ref $($mutability)* destination,
ref $($mutability)* cleanup } => {
cleanup } => {
self.visit_operand(func);
for arg in args {
self.visit_operand(arg);
......
......@@ -43,13 +43,6 @@ pub fn push(&mut self, block: BasicBlock, statement: Statement<'tcx>) {
self.block_data_mut(block).statements.push(statement);
}
pub fn push_drop(&mut self, block: BasicBlock, span: Span, lvalue: &Lvalue<'tcx>) {
self.push(block, Statement {
span: span,
kind: StatementKind::Drop(lvalue.clone())
});
}
pub fn push_assign(&mut self,
block: BasicBlock,
span: Span,
......
......@@ -188,7 +188,7 @@ pub fn into_expr(&mut self,
// operators like x[j] = x[i].
let rhs = unpack!(block = this.as_operand(block, rhs));
let lhs = unpack!(block = this.as_lvalue(block, lhs));
this.cfg.push_drop(block, expr_span, &lhs);
unpack!(block = this.build_drop(block, lhs.clone()));
this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs));
block.unit()
}
......
此差异已折叠。
......@@ -42,16 +42,16 @@ pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<'tcx>>) -> Blo
None => {
let (extent, _) = stmt_lists.pop().unwrap();
if let Some(extent) = extent {
this.pop_scope(extent, block);
unpack!(block = this.pop_scope(extent, block));
}
continue
}
};
let Stmt { span, kind } = this.hir.mirror(stmt);
let Stmt { span: _, kind } = this.hir.mirror(stmt);
match kind {
StmtKind::Let { remainder_scope, init_scope, pattern, initializer, stmts } => {
this.push_scope(remainder_scope, block);
this.push_scope(remainder_scope);
stmt_lists.push((Some(remainder_scope), stmts.into_iter()));
unpack!(block = this.in_scope(init_scope, block, move |this| {
// FIXME #30046 ^~~~
......@@ -72,7 +72,7 @@ pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<'tcx>>) -> Blo
let expr = this.hir.mirror(expr);
let temp = this.temp(expr.ty.clone());
unpack!(block = this.into(&temp, block, expr));
this.cfg.push_drop(block, span, &temp);
unpack!(block = this.build_drop(block, temp));
block.unit()
}));
}
......
......@@ -69,9 +69,6 @@ fn erase_regions_statement(&mut self,
self.erase_regions_lvalue(lvalue);
self.erase_regions_rvalue(rvalue);
}
StatementKind::Drop(ref mut lvalue) => {
self.erase_regions_lvalue(lvalue);
}
}
}
......@@ -93,6 +90,9 @@ fn erase_regions_terminator(&mut self,
self.erase_regions_lvalue(discr);
*switch_ty = self.tcx.erase_regions(switch_ty);
},
Terminator::Drop { ref mut value, .. } => {
self.erase_regions_lvalue(value);
}
Terminator::Call { ref mut func, ref mut args, ref mut destination, .. } => {
if let Some((ref mut destination, _)) = *destination {
self.erase_regions_lvalue(destination);
......
......@@ -18,10 +18,11 @@
use trans::build;
use trans::common::{self, Block, LandingPad};
use trans::debuginfo::DebugLoc;
use trans::Disr;
use trans::foreign;
use trans::glue;
use trans::type_of;
use trans::type_::Type;
use trans::Disr;
use super::MirContext;
use super::operand::OperandValue::{FatPtr, Immediate, Ref};
......@@ -94,6 +95,13 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
}
mir::Terminator::Drop { ref value, target, unwind: _ } => {
let lvalue = self.trans_lvalue(bcx, value);
// FIXME: this does not account for possibility of unwinding (and totally should).
glue::drop_ty(bcx, lvalue.llval, lvalue.ty.to_ty(bcx.tcx()), DebugLoc::None);
build::Br(bcx, self.llblock(target), DebugLoc::None);
}
mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => {
// Create the callee. This will always be a fn ptr and hence a kind of scalar.
let callee = self.trans_operand(bcx, func);
......
......@@ -10,8 +10,6 @@
use rustc::mir::repr as mir;
use trans::common::Block;
use trans::debuginfo::DebugLoc;
use trans::glue;
use super::MirContext;
use super::TempRef;
......@@ -50,12 +48,6 @@ pub fn trans_statement(&mut self,
}
}
}
mir::StatementKind::Drop(ref lvalue) => {
let tr_lvalue = self.trans_lvalue(bcx, lvalue);
let ty = tr_lvalue.ty.to_ty(bcx.tcx());
glue::drop_ty(bcx, tr_lvalue.llval, ty, DebugLoc::None)
}
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册