diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 2fc585c7c795e..9ed3d2c3057d3 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1298,22 +1298,29 @@ pub fn init_zero_mem<'blk, 'tcx>(cx: Block<'blk, 'tcx>, llptr: ValueRef, t: Ty<' fn memfill<'a, 'tcx>(b: &Builder<'a, 'tcx>, llptr: ValueRef, ty: Ty<'tcx>, byte: u8) { let _icx = push_ctxt("memfill"); let ccx = b.ccx; - let llty = type_of::type_of(ccx, ty); - let ptr_width = &ccx.sess().target.target.target_pointer_width[..]; - let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); - - let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key); let llptr = b.pointercast(llptr, Type::i8(ccx).ptr_to()); let llzeroval = C_u8(ccx, byte); let size = machine::llsize_of(ccx, llty); let align = C_i32(ccx, type_of::align_of(ccx, ty) as i32); - let volatile = C_bool(ccx, false); - b.call(llintrinsicfn, - &[llptr, llzeroval, size, align, volatile], - None, None); + call_memset(b, llptr, llzeroval, size, align, false); } +pub fn call_memset<'bcx, 'tcx>(b: &Builder<'bcx, 'tcx>, + ptr: ValueRef, + fill_byte: ValueRef, + size: ValueRef, + align: ValueRef, + volatile: bool) { + let ccx = b.ccx; + let ptr_width = &ccx.sess().target.target.target_pointer_width[..]; + let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); + let llintrinsicfn = ccx.get_intrinsic(&intrinsic_key); + let volatile = C_bool(ccx, volatile); + b.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None, None); +} + + /// In general, when we create an scratch value in an alloca, the /// creator may not know if the block (that initializes the scratch /// with the desired value) actually dominates the cleanup associated diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 5be585c4189e1..ce772dc641479 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -20,7 +20,6 @@ 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; @@ -96,35 +95,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Terminator::Drop { ref value, target, unwind } => { - let lvalue = self.trans_lvalue(bcx, value); - let ty = lvalue.ty.to_ty(bcx.tcx()); - // Double check for necessity to drop - if !glue::type_needs_drop(bcx.tcx(), ty) { - build::Br(bcx, self.llblock(target), DebugLoc::None); - return; - } - let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); - let drop_ty = glue::get_drop_glue_type(bcx.ccx(), ty); - let llvalue = if drop_ty != ty { - build::PointerCast(bcx, lvalue.llval, - type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) - } else { - lvalue.llval - }; - if let Some(unwind) = unwind { - let uwbcx = self.bcx(unwind); - let unwind = self.make_landing_pad(uwbcx); - build::Invoke(bcx, - drop_fn, - &[llvalue], - self.llblock(target), - unwind.llbb, - None, - DebugLoc::None); - } else { - build::Call(bcx, drop_fn, &[llvalue], None, DebugLoc::None); - build::Br(bcx, self.llblock(target), DebugLoc::None); - } + self.trans_drop(bcx, value, target, unwind); } mir::Terminator::Call { ref func, ref args, ref destination, ref cleanup } => { @@ -287,7 +258,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> { + pub fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> { let bcx = cleanup.fcx.new_block("cleanup", None); // FIXME(#30941) this doesn't handle msvc-style exceptions *bcx.lpad.borrow_mut() = Some(LandingPad::gnu()); @@ -314,11 +285,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { + pub fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { self.blocks[bb.index()] } - fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef { + pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef { self.blocks[bb.index()].llbb } } diff --git a/src/librustc_trans/trans/mir/drop.rs b/src/librustc_trans/trans/mir/drop.rs new file mode 100644 index 0000000000000..256e49d601f03 --- /dev/null +++ b/src/librustc_trans/trans/mir/drop.rs @@ -0,0 +1,104 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use rustc::middle::ty::Ty; +use rustc::mir::repr as mir; +use trans::adt; +use trans::base; +use trans::build; +use trans::common::{self, Block}; +use trans::debuginfo::DebugLoc; +use trans::glue; +use trans::machine; +use trans::type_of; +use trans::type_::Type; + +use super::{MirContext, TempRef}; + +impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_drop(&mut self, + bcx: Block<'bcx, 'tcx>, + value: &mir::Lvalue<'tcx>, + target: mir::BasicBlock, + unwind: Option) { + let lvalue = self.trans_lvalue(bcx, value); + let ty = lvalue.ty.to_ty(bcx.tcx()); + // Double check for necessity to drop + if !glue::type_needs_drop(bcx.tcx(), ty) { + build::Br(bcx, self.llblock(target), DebugLoc::None); + return; + } + let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); + let drop_ty = glue::get_drop_glue_type(bcx.ccx(), ty); + let llvalue = if drop_ty != ty { + build::PointerCast(bcx, lvalue.llval, + type_of::type_of(bcx.ccx(), drop_ty).ptr_to()) + } else { + lvalue.llval + }; + if let Some(unwind) = unwind { + // block cannot be cleanup in this case, so a regular block is fine + let intermediate_bcx = bcx.fcx.new_block("", None); + let uwbcx = self.bcx(unwind); + let unwind = self.make_landing_pad(uwbcx); + // FIXME: it could be possible to do zeroing before invoking here if the drop glue + // didn’t code in the checks inside itself. + build::Invoke(bcx, + drop_fn, + &[llvalue], + intermediate_bcx.llbb, + unwind.llbb, + None, + DebugLoc::None); + // FIXME: perhaps we also should fill inside failed branch? We do not want to re-drop a + // failed drop again by mistake. (conflicts with MSVC SEH if we don’t want to introduce + // a heap of hacks) + self.drop_fill(intermediate_bcx, lvalue.llval, ty); + build::Br(intermediate_bcx, self.llblock(target), DebugLoc::None); + } else { + build::Call(bcx, drop_fn, &[llvalue], None, DebugLoc::None); + self.drop_fill(bcx, lvalue.llval, ty); + build::Br(bcx, self.llblock(target), DebugLoc::None); + } + } + + pub fn drop_fill(&mut self, bcx: Block<'bcx, 'tcx>, value: ValueRef, ty: Ty<'tcx>) { + let llty = type_of::type_of(bcx.ccx(), ty); + let llptr = build::PointerCast(bcx, value, Type::i8(bcx.ccx()).ptr_to()); + let filling = common::C_u8(bcx.ccx(), adt::DTOR_DONE); + let size = machine::llsize_of(bcx.ccx(), llty); + let align = common::C_u32(bcx.ccx(), machine::llalign_of_min(bcx.ccx(), llty)); + base::call_memset(&build::B(bcx), llptr, filling, size, align, false); + } + + pub fn set_operand_dropped(&mut self, + bcx: Block<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>) { + match *operand { + mir::Operand::Constant(_) => return, + mir::Operand::Consume(ref lvalue) => { + if let mir::Lvalue::Temp(idx) = *lvalue { + if let TempRef::Operand(..) = self.temps[idx as usize] { + return // we do not handle these, should we? + } + } + let lvalue = self.trans_lvalue(bcx, lvalue); + let ty = lvalue.ty.to_ty(bcx.tcx()); + if !glue::type_needs_drop(bcx.tcx(), ty) || + common::type_is_fat_ptr(bcx.tcx(), ty) { + return + } else { + self.drop_fill(bcx, lvalue.llval, ty); + } + } + } + } +} diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index b19ecc45a4e5e..37c6d3975675e 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -196,6 +196,7 @@ mod analyze; mod block; mod constant; mod did; +mod drop; mod lvalue; mod operand; mod rvalue; diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index 114e78b05bddd..45cbe2e081f19 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -147,22 +147,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - pub fn trans_operand_into(&mut self, - bcx: Block<'bcx, 'tcx>, - lldest: ValueRef, - operand: &mir::Operand<'tcx>) - { - debug!("trans_operand_into(lldest={}, operand={:?})", - bcx.val_to_string(lldest), - operand); - - // FIXME: consider not copying constants through the - // stack. - - let o = self.trans_operand(bcx, operand); - self.store_operand(bcx, lldest, o); - } - pub fn store_operand(&mut self, bcx: Block<'bcx, 'tcx>, lldest: ValueRef, diff --git a/src/librustc_trans/trans/mir/rvalue.rs b/src/librustc_trans/trans/mir/rvalue.rs index e280eff34c894..23cf4d429b6a4 100644 --- a/src/librustc_trans/trans/mir/rvalue.rs +++ b/src/librustc_trans/trans/mir/rvalue.rs @@ -44,7 +44,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { match *rvalue { mir::Rvalue::Use(ref operand) => { - self.trans_operand_into(bcx, dest.llval, operand); + // FIXME: consider not copying constants through stack. (fixable by translating + // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) + let tr_operand = self.trans_operand(bcx, operand); + self.store_operand(bcx, dest.llval, tr_operand); + self.set_operand_dropped(bcx, operand); bcx } @@ -167,11 +171,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); match *rvalue { - mir::Rvalue::Use(ref operand) => { - let operand = self.trans_operand(bcx, operand); - (bcx, operand) - } - mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => { let operand = self.trans_operand(bcx, operand); debug!("cast operand is {}", operand.repr(bcx)); @@ -401,6 +400,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }) } + mir::Rvalue::Use(..) | mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) | mir::Rvalue::Slice { .. } | @@ -511,7 +511,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { match *rvalue { - mir::Rvalue::Use(..) | // (*) mir::Rvalue::Ref(..) | mir::Rvalue::Len(..) | mir::Rvalue::Cast(..) | // (*) @@ -519,6 +518,7 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { mir::Rvalue::UnaryOp(..) | mir::Rvalue::Box(..) => true, + mir::Rvalue::Use(..) | // (**) mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) | mir::Rvalue::Slice { .. } | @@ -527,4 +527,6 @@ pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool { } // (*) this is only true if the type is suitable + // (**) we need to zero-out the old value before moving, so we are restricted to either + // ensuring all users of `Use` set it themselves or not allowing to “create” operand for it. } diff --git a/src/test/run-fail/mir_dynamic_drops_1.rs b/src/test/run-fail/mir_dynamic_drops_1.rs new file mode 100644 index 0000000000000..07cc066355a4b --- /dev/null +++ b/src/test/run-fail/mir_dynamic_drops_1.rs @@ -0,0 +1,43 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:drop 1 +// error-pattern:drop 2 +use std::io::{self, Write}; + + +/// Structure which will not allow to be dropped twice. +struct Droppable<'a>(&'a mut bool, u32); +impl<'a> Drop for Droppable<'a> { + fn drop(&mut self) { + if *self.0 { + writeln!(io::stderr(), "{} dropped twice", self.1); + ::std::process::exit(1); + } + writeln!(io::stderr(), "drop {}", self.1); + *self.0 = true; + } +} + +#[rustc_mir] +fn mir(){ + let mut b1 = false; + let mut b2 = false; + let x = Droppable(&mut b1, 1); + let y = Droppable(&mut b2, 2); + let mut z = x; + let k = y; + z = k; +} + +fn main() { + mir(); + panic!(); +} diff --git a/src/test/run-fail/mir_dynamic_drops_2.rs b/src/test/run-fail/mir_dynamic_drops_2.rs new file mode 100644 index 0000000000000..655991f944c14 --- /dev/null +++ b/src/test/run-fail/mir_dynamic_drops_2.rs @@ -0,0 +1,40 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:drop 1 +use std::io::{self, Write}; + + +/// Structure which will not allow to be dropped twice. +struct Droppable<'a>(&'a mut bool, u32); +impl<'a> Drop for Droppable<'a> { + fn drop(&mut self) { + if *self.0 { + writeln!(io::stderr(), "{} dropped twice", self.1); + ::std::process::exit(1); + } + writeln!(io::stderr(), "drop {}", self.1); + *self.0 = true; + } +} + +#[rustc_mir] +fn mir(d: Droppable){ + loop { + let x = d; + break; + } +} + +fn main() { + let mut d1 = false; + mir(Droppable(&mut d1, 1)); + panic!(); +} diff --git a/src/test/run-fail/mir_dynamic_drops_3.rs b/src/test/run-fail/mir_dynamic_drops_3.rs new file mode 100644 index 0000000000000..058f3decbfdb6 --- /dev/null +++ b/src/test/run-fail/mir_dynamic_drops_3.rs @@ -0,0 +1,46 @@ +// Copyright 2016 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:unwind happens +// error-pattern:drop 3 +// error-pattern:drop 2 +// error-pattern:drop 1 +use std::io::{self, Write}; + + +/// Structure which will not allow to be dropped twice. +struct Droppable<'a>(&'a mut bool, u32); +impl<'a> Drop for Droppable<'a> { + fn drop(&mut self) { + if *self.0 { + writeln!(io::stderr(), "{} dropped twice", self.1); + ::std::process::exit(1); + } + writeln!(io::stderr(), "drop {}", self.1); + *self.0 = true; + } +} + +fn may_panic<'a>() -> Droppable<'a> { + panic!("unwind happens"); +} + +#[rustc_mir] +fn mir(d: Droppable){ + let mut d1 = false; + let mut d2 = false; + let y = Droppable(&mut d2, 2); + let x = [Droppable(&mut d1, 1), y, d, may_panic()]; +} + +fn main() { + let mut d3 = false; + mir(Droppable(&mut d3, 3)); +}