From de7cb0fdd69c95158d217b9a913f1e25f3bfeef0 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 17 May 2016 01:06:52 +0300 Subject: [PATCH 01/11] introduce DropAndReplace for translating assignments this introduces a DropAndReplace terminator as a fix to #30380. That terminator is suppsoed to be translated by desugaring during drop elaboration, which is not implemented in this commit, so this breaks `-Z orbit` temporarily. --- src/librustc/mir/repr.rs | 36 +++++++++++++++---- src/librustc/mir/visit.rs | 14 ++++++-- .../borrowck/mir/dataflow/mod.rs | 11 ++++-- .../borrowck/mir/gather_moves.rs | 12 +++++-- src/librustc_borrowck/borrowck/mir/mod.rs | 16 ++++++--- src/librustc_mir/build/expr/stmt.rs | 28 +++++++-------- src/librustc_mir/build/scope.rs | 33 +++++++++++++---- .../transform/break_cleanup_edges.rs | 4 ++- src/librustc_mir/transform/no_landing_pads.rs | 5 ++- src/librustc_mir/transform/promote_consts.rs | 2 +- src/librustc_mir/transform/qualify_consts.rs | 1 + src/librustc_mir/transform/type_check.rs | 14 ++++++++ src/librustc_trans/mir/block.rs | 8 +++-- 13 files changed, 139 insertions(+), 45 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index f9a671435ffdb..db4c0c1e9eb77 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -330,11 +330,19 @@ pub enum TerminatorKind<'tcx> { /// Drop the Lvalue Drop { - value: Lvalue<'tcx>, + location: Lvalue<'tcx>, target: BasicBlock, unwind: Option }, + /// Drop the Lvalue and assign the new value over it + DropAndReplace { + location: Lvalue<'tcx>, + value: Operand<'tcx>, + target: BasicBlock, + unwind: Option, + }, + /// Block ends with a call of a converging function Call { /// The function that’s being called @@ -373,8 +381,14 @@ impl<'tcx> TerminatorKind<'tcx> { 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(), + DropAndReplace { target, unwind: Some(unwind), .. } | + Drop { target, unwind: Some(unwind), .. } => { + vec![target, unwind].into_cow() + } + DropAndReplace { ref target, unwind: None, .. } | + Drop { ref target, unwind: None, .. } => { + slice::ref_slice(target).into_cow() + } } } @@ -393,8 +407,12 @@ impl<'tcx> TerminatorKind<'tcx> { 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![], + DropAndReplace { ref mut target, unwind: Some(ref mut unwind), .. } | Drop { ref mut target, unwind: Some(ref mut unwind), .. } => vec![target, unwind], - Drop { ref mut target, .. } => vec![target] + DropAndReplace { ref mut target, unwind: None, .. } | + Drop { ref mut target, unwind: None, .. } => { + vec![target] + } } } } @@ -461,7 +479,9 @@ impl<'tcx> TerminatorKind<'tcx> { SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), Return => write!(fmt, "return"), Resume => write!(fmt, "resume"), - Drop { ref value, .. } => write!(fmt, "drop({:?})", value), + Drop { ref location, .. } => write!(fmt, "drop({:?})", location), + DropAndReplace { ref location, ref value, .. } => + write!(fmt, "replace({:?} <- {:?})", location, value), Call { ref func, ref args, ref destination, .. } => { if let Some((ref destination, _)) = *destination { write!(fmt, "{:?} = ", destination)?; @@ -506,8 +526,12 @@ impl<'tcx> TerminatorKind<'tcx> { Call { destination: Some(_), cleanup: None, .. } => vec!["return".into_cow()], Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into_cow()], Call { destination: None, cleanup: None, .. } => vec![], + DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => vec!["return".into_cow()], - Drop { .. } => vec!["return".into_cow(), "unwind".into_cow()], + DropAndReplace { unwind: Some(_), .. } | + Drop { unwind: Some(_), .. } => { + vec!["return".into_cow(), "unwind".into_cow()] + } } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 8846065135253..17a8d040ab478 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -394,10 +394,20 @@ macro_rules! make_mir_visitor { TerminatorKind::Return => { } - TerminatorKind::Drop { ref $($mutability)* value, + TerminatorKind::Drop { ref $($mutability)* location, target, unwind } => { - self.visit_lvalue(value, LvalueContext::Drop); + self.visit_lvalue(location, LvalueContext::Drop); + self.visit_branch(block, target); + unwind.map(|t| self.visit_branch(block, t)); + } + + TerminatorKind::DropAndReplace { ref $($mutability)* location, + ref $($mutability)* value, + target, + unwind } => { + self.visit_lvalue(location, LvalueContext::Drop); + self.visit_operand(value); self.visit_branch(block, target); unwind.map(|t| self.visit_branch(block, t)); } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index b46b6c368a053..99592e5d60fe5 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -444,10 +444,17 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> repr::TerminatorKind::Return | repr::TerminatorKind::Resume => {} repr::TerminatorKind::Goto { ref target } | - repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => { + repr::TerminatorKind::Drop { ref target, location: _, unwind: None } | + + repr::TerminatorKind::DropAndReplace { + ref target, value: _, location: _, unwind: None + } => { self.propagate_bits_into_entry_set_for(in_out, changed, target); } - repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => { + repr::TerminatorKind::Drop { ref target, location: _, unwind: Some(ref unwind) } | + repr::TerminatorKind::DropAndReplace { + ref target, value: _, location: _, unwind: Some(ref unwind) + } => { self.propagate_bits_into_entry_set_for(in_out, changed, target); self.propagate_bits_into_entry_set_for(in_out, changed, unwind); } diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 48511cd5ebc91..fcaa655f749d5 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -671,10 +671,18 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD let _ = discr; } - TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => { + TerminatorKind::Drop { ref location, target: _, unwind: _ } => { let source = Location { block: bb, index: bb_data.statements.len() }; - bb_ctxt.on_move_out_lval(SK::Drop, lval, source); + bb_ctxt.on_move_out_lval(SK::Drop, location, source); + } + TerminatorKind::DropAndReplace { ref location, ref value, .. } => { + let assigned_path = bb_ctxt.builder.move_path_for(location); + bb_ctxt.path_map.fill_to(assigned_path.idx()); + + let source = Location { block: bb, + index: bb_data.statements.len() }; + bb_ctxt.on_operand(SK::Use, value, source); } TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { let source = Location { block: bb, diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 1b9d08bade7c4..38ebecf248ff7 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -309,15 +309,23 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( Some(stmt) => match stmt.kind { repr::StatementKind::Assign(ref lvalue, _) => { debug!("drop_flag_effects: assignment {:?}", stmt); - on_all_children_bits(tcx, mir, move_data, + on_all_children_bits(tcx, mir, move_data, move_data.rev_lookup.find(lvalue), |moi| callback(moi, DropFlagState::Present)) } }, None => { - // terminator - no move-ins except for function return edge - let term = bb.terminator(); - debug!("drop_flag_effects: terminator {:?}", term); + debug!("drop_flag_effects: replace {:?}", bb.terminator()); + match bb.terminator().kind { + repr::TerminatorKind::DropAndReplace { ref location, .. } => { + on_all_children_bits(tcx, mir, move_data, + move_data.rev_lookup.find(location), + |moi| callback(moi, DropFlagState::Present)) + } + _ => { + // other terminators do not contain move-ins + } + } } } } diff --git a/src/librustc_mir/build/expr/stmt.rs b/src/librustc_mir/build/expr/stmt.rs index 9629396f48b50..3324467e70d1d 100644 --- a/src/librustc_mir/build/expr/stmt.rs +++ b/src/librustc_mir/build/expr/stmt.rs @@ -34,29 +34,25 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let scope_id = this.innermost_scope_id(); let lhs_span = lhs.span; - let lhs_ty = lhs.ty; - let rhs_ty = rhs.ty; - - let lhs_needs_drop = this.hir.needs_drop(lhs_ty); - let rhs_needs_drop = this.hir.needs_drop(rhs_ty); - // Note: we evaluate assignments right-to-left. This // is better for borrowck interaction with overloaded // operators like x[j] = x[i]. // Generate better code for things that don't need to be // dropped. - let rhs = if lhs_needs_drop || rhs_needs_drop { - let op = unpack!(block = this.as_operand(block, rhs)); - Rvalue::Use(op) + if this.hir.needs_drop(lhs.ty) { + let rhs = unpack!(block = this.as_operand(block, rhs)); + let lhs = unpack!(block = this.as_lvalue(block, lhs)); + unpack!(block = this.build_drop_and_replace( + block, lhs_span, lhs, rhs + )); + block.unit() } else { - unpack!(block = this.as_rvalue(block, rhs)) - }; - - let lhs = unpack!(block = this.as_lvalue(block, lhs)); - unpack!(block = this.build_drop(block, lhs_span, lhs.clone(), lhs_ty)); - this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs); - block.unit() + let rhs = unpack!(block = this.as_rvalue(block, rhs)); + let lhs = unpack!(block = this.as_lvalue(block, lhs)); + this.cfg.push_assign(block, scope_id, expr_span, &lhs, rhs); + block.unit() + } } ExprKind::AssignOp { op, lhs, rhs } => { // FIXME(#28160) there is an interesting semantics diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 071c8d618c845..cd81fc764f4af 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -139,7 +139,7 @@ struct DropData<'tcx> { span: Span, /// lvalue to drop - value: Lvalue<'tcx>, + location: Lvalue<'tcx>, /// The cached block for the cleanups-on-diverge path. This block /// contains code to run the current drop and all the preceding @@ -402,7 +402,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // the drop that comes before it in the vector. scope.drops.push(DropData { span: span, - value: lvalue.clone(), + location: lvalue.clone(), cached_block: None }); return; @@ -497,7 +497,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { pub fn build_drop(&mut self, block: BasicBlock, span: Span, - value: Lvalue<'tcx>, + location: Lvalue<'tcx>, ty: Ty<'tcx>) -> BlockAnd<()> { if !self.hir.needs_drop(ty) { return block.unit(); @@ -509,7 +509,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { scope_id, span, TerminatorKind::Drop { - value: value, + location: location, target: next_target, unwind: diverge_target, }); @@ -517,6 +517,27 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { } + + pub fn build_drop_and_replace(&mut self, + block: BasicBlock, + span: Span, + location: Lvalue<'tcx>, + value: Operand<'tcx>) -> BlockAnd<()> { + let scope_id = self.innermost_scope_id(); + let next_target = self.cfg.start_new_block(); + let diverge_target = self.diverge_cleanup(); + self.cfg.terminate(block, + scope_id, + span, + TerminatorKind::DropAndReplace { + location: location, + value: value, + target: next_target, + unwind: diverge_target, + }); + next_target.unit() + } + // Panicking // ========= // FIXME: should be moved into their own module @@ -653,7 +674,7 @@ fn build_scope_drops<'tcx>(cfg: &mut CFG<'tcx>, }); let next = cfg.start_new_block(); cfg.terminate(block, scope.id, drop_data.span, TerminatorKind::Drop { - value: drop_data.value.clone(), + location: drop_data.location.clone(), target: next, unwind: on_diverge }); @@ -709,7 +730,7 @@ fn build_diverge_scope<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, scope.id, drop_data.span, TerminatorKind::Drop { - value: drop_data.value.clone(), + location: drop_data.location.clone(), target: target, unwind: None }); diff --git a/src/librustc_mir/transform/break_cleanup_edges.rs b/src/librustc_mir/transform/break_cleanup_edges.rs index 0eb6223a71e54..4902d31cf4d7a 100644 --- a/src/librustc_mir/transform/break_cleanup_edges.rs +++ b/src/librustc_mir/transform/break_cleanup_edges.rs @@ -105,7 +105,9 @@ impl Pass for BreakCleanupEdges {} fn term_is_invoke(term: &Terminator) -> bool { match term.kind { TerminatorKind::Call { cleanup: Some(_), .. } | - TerminatorKind::Drop { unwind: Some(_), .. } => true, + // FIXME: not sure whether we need this one + TerminatorKind::Drop { unwind: Some(_), .. } | + TerminatorKind::DropAndReplace { .. } => true, _ => false } } diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index de05032fa5586..67710c4328569 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -29,12 +29,11 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { TerminatorKind::SwitchInt { .. } => { /* nothing to do */ }, + TerminatorKind::Call { cleanup: ref mut unwind, .. } | + TerminatorKind::DropAndReplace { ref mut unwind, .. } | TerminatorKind::Drop { ref mut unwind, .. } => { unwind.take(); }, - TerminatorKind::Call { ref mut cleanup, .. } => { - cleanup.take(); - }, } self.super_terminator(bb, terminator); } diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs index 431568b004d3d..d81c4e2dfb68e 100644 --- a/src/librustc_mir/transform/promote_consts.rs +++ b/src/librustc_mir/transform/promote_consts.rs @@ -399,7 +399,7 @@ pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>, }); let terminator = block.terminator_mut(); match terminator.kind { - TerminatorKind::Drop { value: Lvalue::Temp(index), target, .. } => { + TerminatorKind::Drop { location: Lvalue::Temp(index), target, .. } => { if promoted(index) { terminator.kind = TerminatorKind::Goto { target: target diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 2e4400c834f23..18a1f1595f3c3 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -422,6 +422,7 @@ impl<'a, 'tcx> Qualifier<'a, 'tcx, 'tcx> { TerminatorKind::Switch {..} | TerminatorKind::SwitchInt {..} | + TerminatorKind::DropAndReplace { .. } | TerminatorKind::Resume => None, TerminatorKind::Return => { diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 80c56a5dc08f1..7a41211381cb9 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -363,6 +363,20 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { // no checks needed for these } + + TerminatorKind::DropAndReplace { + ref location, + ref value, + .. + } => { + let lv_ty = mir.lvalue_ty(tcx, location).to_ty(tcx); + let rv_ty = mir.operand_ty(tcx, value); + if let Err(terr) = self.sub_types(self.last_span, rv_ty, lv_ty) { + span_mirbug!(self, term, "bad DropAndReplace ({:?} = {:?}): {:?}", + lv_ty, rv_ty, terr); + } + } + TerminatorKind::If { ref cond, .. } => { let cond_ty = mir.operand_ty(tcx, cond); match cond_ty.sty { diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index 4e3386bc73677..fb93e487f3bb2 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -143,8 +143,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }) } - mir::TerminatorKind::Drop { ref value, target, unwind } => { - let lvalue = self.trans_lvalue(&bcx, value); + mir::TerminatorKind::Drop { ref location, target, unwind } => { + let lvalue = self.trans_lvalue(&bcx, location); let ty = lvalue.ty.to_ty(bcx.tcx()); // Double check for necessity to drop if !glue::type_needs_drop(bcx.tcx(), ty) { @@ -177,6 +177,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } + mir::TerminatorKind::DropAndReplace { .. } => { + bug!("undesugared DropAndReplace in trans: {:?}", data); + } + mir::TerminatorKind::Call { ref func, ref args, ref destination, ref cleanup } => { // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.trans_operand(&bcx, func); From a091cfd4f3be8677481a3a502bd96bdebd0de1bb Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 17 May 2016 02:26:18 +0300 Subject: [PATCH 02/11] implement drop elaboration Fixes #30380 --- src/librustc/infer/mod.rs | 18 + .../borrowck/mir/dataflow/mod.rs | 6 + .../borrowck/mir/elaborate_drops.rs | 1038 +++++++++++++++++ src/librustc_borrowck/borrowck/mir/mod.rs | 35 +- src/librustc_borrowck/borrowck/mir/patch.rs | 184 +++ src/librustc_borrowck/borrowck/mod.rs | 2 + src/librustc_borrowck/lib.rs | 2 +- src/librustc_driver/driver.rs | 4 + src/test/run-fail/issue-30380.rs | 44 + src/test/run-pass/dynamic-drop.rs | 100 ++ 10 files changed, 1431 insertions(+), 2 deletions(-) create mode 100644 src/librustc_borrowck/borrowck/mir/elaborate_drops.rs create mode 100644 src/librustc_borrowck/borrowck/mir/patch.rs create mode 100644 src/test/run-fail/issue-30380.rs create mode 100644 src/test/run-pass/dynamic-drop.rs diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 7c9c52baa63e4..d22b7bd6d5a9f 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -613,6 +613,24 @@ impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { value.trans_normalize(&infcx) }) } + + pub fn normalize_associated_type_in_env( + self, value: &T, env: &'a ty::ParameterEnvironment<'tcx> + ) -> T + where T: TransNormalize<'tcx> + { + debug!("normalize_associated_type_in_env(t={:?})", value); + + let value = self.erase_regions(value); + + if !value.has_projection_types() { + return value; + } + + self.infer_ctxt(None, Some(env.clone()), ProjectionMode::Any).enter(|infcx| { + value.trans_normalize(&infcx) + }) + } } impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 99592e5d60fe5..113d3ff8512b1 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -200,6 +200,12 @@ impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> pub struct DataflowResults(DataflowState) where O: BitDenotation; +impl DataflowResults { + pub fn sets(&self) -> &AllSets { + &self.0.sets + } +} + // FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait // references it in a method signature. Look into using `pub(crate)` to address this. pub struct DataflowState diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs new file mode 100644 index 0000000000000..e299d47aee61c --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -0,0 +1,1038 @@ +// 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. + +use bitslice::BitSlice; +use super::gather_moves::{MoveData, MovePathIndex, MovePathContent, Location}; +use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; +use super::dataflow::{DataflowResults}; +use super::{drop_flag_effects_for_location, on_all_children_bits}; +use super::{DropFlagState}; +use super::patch::MirPatch; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::subst::{Subst, Substs, VecPerParamSpace}; +use rustc::mir::repr::*; +use rustc::mir::transform::{Pass, MirPass, MirSource}; +use rustc::middle::const_val::ConstVal; +use rustc::middle::lang_items; +use rustc::util::nodemap::FnvHashMap; +use rustc_mir::pretty; +use syntax::codemap::Span; + +use std::fmt; +use std::u32; + +pub struct ElaborateDrops; + +impl<'tcx> MirPass<'tcx> for ElaborateDrops { + fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, mir: &mut Mir<'tcx>) + { + debug!("elaborate_drops({:?} @ {:?})", src, mir.span); + match src { + MirSource::Fn(..) => {}, + _ => return + } + let id = src.item_id(); + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let move_data = MoveData::gather_moves(mir, tcx); + let elaborate_patch = { + let mir = &*mir; + let ((_, _, move_data), flow_inits) = + super::do_dataflow(tcx, mir, id, &[], (tcx, mir, move_data), + MaybeInitializedLvals::default()); + let ((_, _, move_data), flow_uninits) = + super::do_dataflow(tcx, mir, id, &[], (tcx, mir, move_data), + MaybeUninitializedLvals::default()); + + match (tcx, mir, move_data) { + ref ctxt => ElaborateDropsCtxt { + ctxt: ctxt, + param_env: ¶m_env, + flow_inits: flow_inits, + flow_uninits: flow_uninits, + drop_flags: FnvHashMap(), + patch: MirPatch::new(mir), + }.elaborate() + } + }; + pretty::dump_mir(tcx, "elaborate_drops", &0, src, mir, None); + elaborate_patch.apply(mir); + pretty::dump_mir(tcx, "elaborate_drops", &1, src, mir, None); + } +} + +impl Pass for ElaborateDrops {} + +struct InitializationData { + live: Vec, + dead: Vec +} + +impl InitializationData { + fn apply_location<'a,'tcx>(&mut self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + loc: Location) + { + drop_flag_effects_for_location(tcx, mir, move_data, loc, |path, df| { + debug!("at location {:?}: setting {:?} to {:?}", + loc, path, df); + match df { + DropFlagState::Live => { + self.live.set_bit(path.idx()); + self.dead.clear_bit(path.idx()); + } + DropFlagState::Dead => { + self.dead.set_bit(path.idx()); + self.live.clear_bit(path.idx()); + } + } + }); + } + + fn state(&self, path: MovePathIndex) -> (bool, bool) { + (self.live.get_bit(path.idx()), self.dead.get_bit(path.idx())) + } +} + +impl fmt::Debug for InitializationData { + fn fmt(&self, _f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + Ok(()) + } +} + +struct ElaborateDropsCtxt<'a, 'tcx: 'a> { + ctxt: &'a (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>), + param_env: &'a ty::ParameterEnvironment<'tcx>, + flow_inits: DataflowResults>, + flow_uninits: DataflowResults>, + drop_flags: FnvHashMap, + patch: MirPatch<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +struct DropCtxt<'a, 'tcx: 'a> { + span: Span, + scope: ScopeId, + is_cleanup: bool, + + init_data: &'a InitializationData, + + lvalue: &'a Lvalue<'tcx>, + path: MovePathIndex, + succ: BasicBlock, + unwind: Option +} + +impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { + fn tcx(&self) -> TyCtxt<'b, 'tcx, 'tcx> { self.ctxt.0 } + fn mir(&self) -> &'b Mir<'tcx> { self.ctxt.1 } + fn move_data(&self) -> &'b MoveData<'tcx> { &self.ctxt.2 } + + fn initialization_data_at(&self, loc: Location) -> InitializationData { + let mut data = InitializationData { + live: self.flow_inits.sets().on_entry_set_for(loc.block.index()) + .to_owned(), + dead: self.flow_uninits.sets().on_entry_set_for(loc.block.index()) + .to_owned(), + }; + for stmt in 0..loc.index { + data.apply_location(self.ctxt.0, self.ctxt.1, &self.ctxt.2, + Location { block: loc.block, index: stmt }); + } + data + } + + fn create_drop_flag(&mut self, index: MovePathIndex) { + let tcx = self.tcx(); + let patch = &mut self.patch; + self.drop_flags.entry(index).or_insert_with(|| { + patch.new_temp(tcx.types.bool) + }); + } + + fn drop_flag(&mut self, index: MovePathIndex) -> Option> { + self.drop_flags.get(&index).map(|t| Lvalue::Temp(*t)) + } + + /// create a patch that elaborates all drops in the input + /// MIR. + fn elaborate(mut self) -> MirPatch<'tcx> + { + self.collect_drop_flags(); + + self.elaborate_drops(); + + self.drop_flags_on_init(); + self.drop_flags_for_fn_rets(); + self.drop_flags_for_args(); + self.drop_flags_for_locs(); + + self.patch + } + + fn path_needs_drop(&self, path: MovePathIndex) -> bool + { + match self.move_data().move_paths[path].content { + MovePathContent::Lvalue(ref lvalue) => { + let ty = self.mir().lvalue_ty(self.tcx(), lvalue) + .to_ty(self.tcx()); + debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty); + + self.tcx().type_needs_drop_given_env(ty, &self.param_env) + } + _ => false + } + } + + /// Returns whether this lvalue is tracked by drop elaboration. This + /// includes all lvalues, except these behind references or arrays. + /// + /// Lvalues behind references or arrays are not tracked by elaboration + /// and are always assumed to be initialized when accessible. As + /// references and indexes can be reseated, trying to track them + /// can only lead to trouble. + fn lvalue_is_tracked(&self, lv: &Lvalue<'tcx>) -> bool + { + if let &Lvalue::Projection(ref data) = lv { + self.lvalue_contents_are_tracked(&data.base) + } else { + true + } + } + + fn lvalue_contents_are_tracked(&self, lv: &Lvalue<'tcx>) -> bool { + let ty = self.mir().lvalue_ty(self.tcx(), lv).to_ty(self.tcx()); + match ty.sty { + ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { + false + } + _ => self.lvalue_is_tracked(lv) + } + } + + fn collect_drop_flags(&mut self) + { + for bb in self.mir().all_basic_blocks() { + let data = self.mir().basic_block_data(bb); + let terminator = data.terminator(); + let location = match terminator.kind { + TerminatorKind::Drop { ref location, .. } | + TerminatorKind::DropAndReplace { ref location, .. } => location, + _ => continue + }; + + if !self.lvalue_is_tracked(location) { + continue + } + + let init_data = self.initialization_data_at(Location { + block: bb, + index: data.statements.len() + }); + + let path = self.move_data().rev_lookup.find(location); + debug!("collect_drop_flags: {:?}, lv {:?} (index {:?})", + bb, location, path); + + on_all_children_bits(self.tcx(), self.mir(), self.move_data(), path, |child| { + if self.path_needs_drop(child) { + let (maybe_live, maybe_dead) = init_data.state(child); + debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", + child, location, path, (maybe_live, maybe_dead)); + if maybe_live && maybe_dead { + self.create_drop_flag(child) + } + } + }); + } + } + + fn elaborate_drops(&mut self) + { + for bb in self.mir().all_basic_blocks() { + let data = self.mir().basic_block_data(bb); + let loc = Location { block: bb, index: data.statements.len() }; + let terminator = data.terminator(); + + let resume_block = self.patch.resume_block(); + match terminator.kind { + TerminatorKind::Drop { ref location, target, unwind } => { + let init_data = self.initialization_data_at(loc); + let path = self.move_data().rev_lookup.find(location); + self.elaborate_drop(&DropCtxt { + span: terminator.span, + scope: terminator.scope, + is_cleanup: data.is_cleanup, + init_data: &init_data, + lvalue: location, + path: path, + succ: target, + unwind: if data.is_cleanup { + None + } else { + Some(Option::unwrap_or(unwind, resume_block)) + } + }, bb); + } + TerminatorKind::DropAndReplace { ref location, ref value, + target, unwind } => + { + assert!(!data.is_cleanup); + + self.elaborate_replace( + loc, + location, value, + target, unwind + ); + } + _ => continue + } + } + } + + /// Elaborate a MIR `replace` terminator. This instruction + /// is not directly handled by translation, and therefore + /// must be desugared. + /// + /// The desugaring drops the location if needed, and then writes + /// the value (including setting the drop flag) over it in *both* arms. + /// + /// The `replace` terminator can also be called on lvalues that + /// are not tracked by elaboration (for example, + /// `replace x[i] <- tmp0`). The borrow checker requires that + /// these locations are initialized before the assignment, + /// so we just generate an unconditional drop. + fn elaborate_replace( + &mut self, + loc: Location, + location: &Lvalue<'tcx>, + value: &Operand<'tcx>, + target: BasicBlock, + unwind: Option) + { + let bb = loc.block; + let data = self.mir().basic_block_data(bb); + let terminator = data.terminator(); + + let unwind = Some(unwind.unwrap_or_else(|| { + // we can't use the resume block directly, because we + // may want to add a drop flag write. + self.jump_to_resume_block(terminator.scope, + terminator.span) + })); + + if !self.lvalue_is_tracked(location) { + // drop and replace behind a pointer/array/whatever. The location + // must be initialized. + debug!("elaborate_drop_and_replace({:?}) - untracked", terminator); + self.patch.patch_terminator(bb, TerminatorKind::Drop { + location: location.clone(), + target: target, + unwind: unwind + }); + } else { + debug!("elaborate_drop_and_replace({:?}) - tracked", terminator); + let init_data = self.initialization_data_at(loc); + let path = self.move_data().rev_lookup.find(location); + + self.elaborate_drop(&DropCtxt { + span: terminator.span, + scope: terminator.scope, + is_cleanup: data.is_cleanup, + init_data: &init_data, + lvalue: location, + path: path, + succ: target, + unwind: unwind + }, bb); + on_all_children_bits(self.tcx(), self.mir(), self.move_data(), path, |child| { + self.set_drop_flag(Location { block: target, index: 0 }, + child, DropFlagState::Live); + if let Some(unwind) = unwind { + self.set_drop_flag(Location { block: unwind, index: 0 }, + child, DropFlagState::Live); + } + }); + } + + self.patch.add_assign(Location { block: target, index: 0 }, + location.clone(), Rvalue::Use(value.clone())); + if let Some(unwind) = unwind { + self.patch.add_assign(Location { block: unwind, index: 0 }, + location.clone(), Rvalue::Use(value.clone())); + } + } + + /// This elaborates a single drop instruction, located at `bb`, and + /// patches over it. + /// + /// The elaborated drop checks the drop flags to only drop what + /// is initialized. + /// + /// In addition, the relevant drop flags also need to be cleared + /// to avoid double-drops. However, in the middle of a complex + /// drop, one must avoid clearing some of the flags before they + /// are read, as that would cause a memory leak. + /// + /// In particular, when dropping an ADT, multiple fields may be + /// joined together under the `rest` subpath. They are all controlled + /// by the primary drop flag, but only the last rest-field dropped + /// should clear it (and it must also not clear anything else). + /// + /// FIXME: I think we should just control the flags externally + /// and then we do not need this machinery. + fn elaborate_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) { + debug!("elaborate_drop({:?})", c); + + let mut some_live = false; + let mut some_dead = false; + let mut children_count = 0; + on_all_children_bits( + self.tcx(), self.mir(), self.move_data(), + c.path, |child| { + if self.path_needs_drop(child) { + let (live, dead) = c.init_data.state(child); + debug!("elaborate_drop: state({:?}) = {:?}", + child, (live, dead)); + some_live |= live; + some_dead |= dead; + children_count += 1; + } + }); + + debug!("elaborate_drop({:?}): live - {:?}", c, + (some_live, some_dead)); + match (some_live, some_dead) { + (false, false) | (false, true) => { + // dead drop - patch it out + self.patch.patch_terminator(bb, TerminatorKind::Goto { + target: c.succ + }); + } + (true, false) => { + // static drop - just set the flag + self.patch.patch_terminator(bb, TerminatorKind::Drop { + location: c.lvalue.clone(), + target: c.succ, + unwind: c.unwind + }); + self.drop_flags_for_drop(c, bb); + } + (true, true) => { + // dynamic drop + let drop_bb = if children_count == 1 || self.must_complete_drop(c) { + self.conditional_drop(c) + } else { + self.open_drop(c) + }; + self.patch.patch_terminator(bb, TerminatorKind::Goto { + target: drop_bb + }); + } + } + } + + /// Return the lvalue and move path for each field of `variant`, + /// (the move path is `None` if the field is a rest field). + fn move_paths_for_fields(&self, + base_lv: &Lvalue<'tcx>, + variant_path: MovePathIndex, + variant: ty::VariantDef<'tcx>, + substs: &'tcx Substs<'tcx>) + -> Vec<(Lvalue<'tcx>, Option)> + { + let move_paths = &self.move_data().move_paths; + variant.fields.iter().enumerate().map(|(i, f)| { + let subpath = + super::move_path_children_matching(move_paths, variant_path, |p| { + match p { + &Projection { + elem: ProjectionElem::Field(idx, _), .. + } => idx.index() == i, + _ => false + } + }); + + let field_ty = + self.tcx().normalize_associated_type_in_env( + &f.ty(self.tcx(), substs), + &self.param_env + ); + (base_lv.clone().field(Field::new(i), field_ty), subpath) + }).collect() + } + + /// Create one-half of the drop ladder for a list of fields, and return + /// the list of steps in it in reverse order. + /// + /// `unwind_ladder` is such a list of steps in reverse order, + /// which is called instead of the next step if the drop unwinds + /// (the first field is never reached). If it is `None`, all + /// unwind targets are left blank. + fn drop_halfladder<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + unwind_ladder: Option>, + succ: BasicBlock, + fields: &[(Lvalue<'tcx>, Option)], + is_cleanup: bool) + -> Vec + { + let mut succ = succ; + let mut unwind_succ = if is_cleanup { + None + } else { + c.unwind + }; + let mut update_drop_flag = true; + + fields.iter().rev().enumerate().map(|(i, &(ref lv, path))| { + let drop_block = match path { + Some(path) => { + debug!("drop_ladder: for std field {} ({:?})", i, lv); + + self.elaborated_drop_block(&DropCtxt { + span: c.span, + scope: c.scope, + is_cleanup: is_cleanup, + init_data: c.init_data, + lvalue: lv, + path: path, + succ: succ, + unwind: unwind_succ, + }) + } + None => { + debug!("drop_ladder: for rest field {} ({:?})", i, lv); + + let blk = self.complete_drop(&DropCtxt { + span: c.span, + scope: c.scope, + is_cleanup: is_cleanup, + init_data: c.init_data, + lvalue: lv, + path: c.path, + succ: succ, + unwind: unwind_succ, + }, update_drop_flag); + + // the drop flag has been updated - updating + // it again would clobber it. + update_drop_flag = false; + + blk + } + }; + + succ = drop_block; + unwind_succ = unwind_ladder.as_ref().map(|p| p[i]); + + drop_block + }).collect() + } + + /// Create a full drop ladder, consisting of 2 connected half-drop-ladders + /// + /// For example, with 3 fields, the drop ladder is + /// + /// .d0: + /// ELAB(drop location.0 [target=.d1, unwind=.c1]) + /// .d1: + /// ELAB(drop location.1 [target=.d2, unwind=.c2]) + /// .d2: + /// ELAB(drop location.2 [target=`c.succ`, unwind=`c.unwind`]) + /// .c1: + /// ELAB(drop location.1 [target=.c2]) + /// .c2: + /// ELAB(drop location.2 [target=`c.unwind]) + fn drop_ladder<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + fields: &[(Lvalue<'tcx>, Option)]) + -> BasicBlock + { + debug!("drop_ladder({:?}, {:?})", c, fields); + let unwind_ladder = if c.is_cleanup { + None + } else { + Some(self.drop_halfladder(c, None, c.unwind.unwrap(), &fields, true)) + }; + + self.drop_halfladder(c, unwind_ladder, c.succ, fields, c.is_cleanup) + .last().cloned().unwrap_or(c.succ) + } + + fn open_drop_for_tuple<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, tys: &[Ty<'tcx>]) + -> BasicBlock + { + debug!("open_drop_for_tuple({:?}, {:?})", c, tys); + + let fields: Vec<_> = tys.iter().enumerate().map(|(i, &ty)| { + (c.lvalue.clone().field(Field::new(i), ty), + super::move_path_children_matching( + &self.move_data().move_paths, c.path, |proj| match proj { + &Projection { + elem: ProjectionElem::Field(f, _), .. + } => f.index() == i, + _ => false + } + )) + }).collect(); + + self.drop_ladder(c, &fields) + } + + fn open_drop_for_box<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, ty: Ty<'tcx>) + -> BasicBlock + { + debug!("open_drop_for_box({:?}, {:?})", c, ty); + + let interior_path = super::move_path_children_matching( + &self.move_data().move_paths, c.path, |proj| match proj { + &Projection { elem: ProjectionElem::Deref, .. } => true, + _ => false + }).unwrap(); + + let interior = c.lvalue.clone().deref(); + let inner_c = DropCtxt { + lvalue: &interior, + unwind: c.unwind.map(|u| { + self.box_free_block(c, ty, u, true) + }), + succ: self.box_free_block(c, ty, c.succ, c.is_cleanup), + path: interior_path, + ..*c + }; + + self.elaborated_drop_block(&inner_c) + } + + fn open_drop_for_variant<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + drop_block: &mut Option, + adt: ty::AdtDef<'tcx>, + substs: &'tcx Substs<'tcx>, + variant_index: usize) + -> BasicBlock + { + let move_paths = &self.move_data().move_paths; + + let subpath = super::move_path_children_matching( + move_paths, c.path, |proj| match proj { + &Projection { + elem: ProjectionElem::Downcast(_, idx), .. + } => idx == variant_index, + _ => false + }); + + if let Some(variant_path) = subpath { + let base_lv = c.lvalue.clone().elem( + ProjectionElem::Downcast(adt, variant_index) + ); + let fields = self.move_paths_for_fields( + &base_lv, + variant_path, + &adt.variants[variant_index], + substs); + self.drop_ladder(c, &fields) + } else { + // variant not found - drop the entire enum + if let None = *drop_block { + *drop_block = Some(self.complete_drop(c, true)); + } + return drop_block.unwrap(); + } + } + + fn open_drop_for_adt<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, + adt: ty::AdtDef<'tcx>, substs: &'tcx Substs<'tcx>) + -> BasicBlock { + debug!("open_drop_for_adt({:?}, {:?}, {:?})", c, adt, substs); + + let mut drop_block = None; + + match adt.variants.len() { + 1 => { + let fields = self.move_paths_for_fields( + c.lvalue, + c.path, + &adt.variants[0], + substs + ); + self.drop_ladder(c, &fields) + } + _ => { + let variant_drops : Vec = + (0..adt.variants.len()).map(|i| { + self.open_drop_for_variant(c, &mut drop_block, + adt, substs, i) + }).collect(); + + // If there are multiple variants, then if something + // is present within the enum the discriminant, tracked + // by the rest path, must be initialized. + // + // Additionally, we do not want to switch on the + // discriminant after it is free-ed, because that + // way lies only trouble. + + let switch_block = self.new_block( + c, c.is_cleanup, TerminatorKind::Switch { + discr: c.lvalue.clone(), + adt_def: adt, + targets: variant_drops + }); + + self.drop_flag_test_block(c, c.is_cleanup, switch_block) + } + } + } + + /// The slow-path - create an "open", elaborated drop for a type + /// which is moved-out-of only partially, and patch `bb` to a jump + /// to it. This must not be called on ADTs with a destructor, + /// as these can't be moved-out-of, except for `Box`, which is + /// special-cased. + /// + /// This creates a "drop ladder" that drops the needed fields of the + /// ADT, both in the success case or if one of the destructors fail. + fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { + let ty = self.mir().lvalue_ty(self.tcx(), c.lvalue).to_ty(self.tcx()); + match ty.sty { + ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { + self.open_drop_for_adt(c, def, substs) + } + ty::TyTuple(tys) | ty::TyClosure(_, ty::ClosureSubsts { + upvar_tys: tys, .. + }) => { + self.open_drop_for_tuple(c, tys) + } + ty::TyBox(ty) => { + self.open_drop_for_box(c, ty) + } + _ => bug!("open drop from non-ADT `{:?}`", ty) + } + } + + /// Return a basic block that drop an lvalue using the context + /// and path in `c`. If `update_drop_flag` is true, also + /// clear `c`. + /// + /// if FLAG(c.path) + /// if(update_drop_flag) FLAG(c.path) = false + /// drop(c.lv) + fn complete_drop<'a>( + &mut self, + c: &DropCtxt<'a, 'tcx>, + update_drop_flag: bool) + -> BasicBlock + { + debug!("complete_drop({:?},{:?})", c, update_drop_flag); + + let drop_block = self.drop_block(c); + if update_drop_flag { + self.set_drop_flag( + Location { block: drop_block, index: 0 }, + c.path, + DropFlagState::Dead + ); + } + + self.drop_flag_test_block(c, c.is_cleanup, drop_block) + } + + /// Create a simple conditional drop. + /// + /// if FLAG(c.lv) + /// FLAGS(c.lv) = false + /// drop(c.lv) + fn conditional_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) + -> BasicBlock + { + debug!("conditional_drop({:?})", c); + let drop_bb = self.drop_block(c); + self.drop_flags_for_drop(c, drop_bb); + + self.drop_flag_test_block(c, c.is_cleanup, drop_bb) + } + + fn new_block<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + is_cleanup: bool, + k: TerminatorKind<'tcx>) + -> BasicBlock + { + self.patch.new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + scope: c.scope, span: c.span, kind: k + }), + is_cleanup: is_cleanup + }) + } + + fn elaborated_drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { + debug!("elaborated_drop_block({:?})", c); + let blk = self.drop_block(c); + self.elaborate_drop(c, blk); + blk + } + + fn drop_flag_test_block<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + is_cleanup: bool, + on_set: BasicBlock) + -> BasicBlock + { + let (maybe_live, maybe_dead) = c.init_data.state(c.path); + debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", + c, is_cleanup, on_set, (maybe_live, maybe_dead)); + + match (maybe_live, maybe_dead) { + (false, _) => c.succ, + (true, false) => on_set, + (true, true) => { + let flag = self.drop_flag(c.path).unwrap(); + self.new_block(c, is_cleanup, TerminatorKind::If { + cond: Operand::Consume(flag), + targets: (on_set, c.succ) + }) + } + } + } + + fn drop_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { + self.new_block(c, c.is_cleanup, TerminatorKind::Drop { + location: c.lvalue.clone(), + target: c.succ, + unwind: c.unwind + }) + } + + fn jump_to_resume_block<'a>(&mut self, scope: ScopeId, span: Span) -> BasicBlock { + let resume_block = self.patch.resume_block(); + self.patch.new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + scope: scope, span: span, kind: TerminatorKind::Goto { + target: resume_block + } + }), + is_cleanup: true + }) + } + + fn box_free_block<'a>( + &mut self, + c: &DropCtxt<'a, 'tcx>, + ty: Ty<'tcx>, + target: BasicBlock, + is_cleanup: bool + ) -> BasicBlock { + let block = self.unelaborated_free_block(c, ty, target, is_cleanup); + self.drop_flag_test_block(c, is_cleanup, block) + } + + fn unelaborated_free_block<'a>( + &mut self, + c: &DropCtxt<'a, 'tcx>, + ty: Ty<'tcx>, + target: BasicBlock, + is_cleanup: bool + ) -> BasicBlock { + let mut statements = vec![]; + if let Some(&flag) = self.drop_flags.get(&c.path) { + statements.push(Statement { + span: c.span, + scope: c.scope, + kind: StatementKind::Assign( + Lvalue::Temp(flag), + self.constant_bool(c.span, false) + ) + }); + } + + let tcx = self.tcx(); + let unit_temp = Lvalue::Temp(self.patch.new_temp(tcx.mk_nil())); + let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem) + .unwrap_or_else(|e| tcx.sess.fatal(&e)); + let substs = tcx.mk_substs(Substs::new( + VecPerParamSpace::new(vec![], vec![], vec![ty]), + VecPerParamSpace::new(vec![], vec![], vec![]) + )); + let fty = tcx.lookup_item_type(free_func).ty.subst(tcx, substs); + + self.patch.new_block(BasicBlockData { + statements: statements, + terminator: Some(Terminator { + scope: c.scope, span: c.span, kind: TerminatorKind::Call { + func: Operand::Constant(Constant { + span: c.span, + ty: fty, + literal: Literal::Item { + def_id: free_func, + substs: substs + } + }), + args: vec![Operand::Consume(c.lvalue.clone())], + destination: Some((unit_temp, target)), + cleanup: None + } + }), + is_cleanup: is_cleanup + }) + } + + fn must_complete_drop<'a>(&self, c: &DropCtxt<'a, 'tcx>) -> bool { + // if we have a destuctor, we must *not* split the drop. + + // dataflow can create unneeded children in some cases + // - be sure to ignore them. + + let ty = self.mir().lvalue_ty(self.tcx(), c.lvalue).to_ty(self.tcx()); + + match ty.sty { + ty::TyStruct(def, _) | ty::TyEnum(def, _) => { + if def.has_dtor() { + self.tcx().sess.span_warn( + c.span, + &format!("dataflow bug??? moving out of type with dtor {:?}", + c)); + true + } else { + false + } + } + _ => false + } + } + + fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { + Rvalue::Use(Operand::Constant(Constant { + span: span, + ty: self.tcx().types.bool, + literal: Literal::Value { value: ConstVal::Bool(val) } + })) + } + + fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) { + if let Some(&flag) = self.drop_flags.get(&path) { + let span = self.patch.context_for_location(self.mir(), loc).0; + let val = self.constant_bool(span, val.value()); + self.patch.add_assign(loc, Lvalue::Temp(flag), val); + } + } + + fn drop_flags_on_init(&mut self) { + let loc = Location { block: START_BLOCK, index: 0 }; + let span = self.patch.context_for_location(self.mir(), loc).0; + let false_ = self.constant_bool(span, false); + for flag in self.drop_flags.values() { + self.patch.add_assign(loc, Lvalue::Temp(*flag), false_.clone()); + } + } + + fn drop_flags_for_fn_rets(&mut self) { + for bb in self.mir().all_basic_blocks() { + let data = self.mir().basic_block_data(bb); + if let TerminatorKind::Call { + destination: Some((ref lv, tgt)), cleanup: Some(_), .. + } = data.terminator().kind { + assert!(!self.patch.is_patched(bb)); + + let loc = Location { block: tgt, index: 0 }; + let path = self.move_data().rev_lookup.find(lv); + on_all_children_bits( + self.tcx(), self.mir(), self.move_data(), path, + |child| self.set_drop_flag(loc, child, DropFlagState::Live) + ); + } + } + } + + fn drop_flags_for_args(&mut self) { + let loc = Location { block: START_BLOCK, index: 0 }; + super::drop_flag_effects_for_function_entry( + self.tcx(), self.mir(), self.move_data(), |path, ds| { + self.set_drop_flag(loc, path, ds); + } + ) + } + + fn drop_flags_for_locs(&mut self) { + // We intentionally iterate only over the *old* basic blocks. + // + // Basic blocks created by drop elaboration update their + // drop flags by themselves, to avoid the drop flags being + // clobbered before they are read. + + for bb in self.mir().all_basic_blocks() { + let data = self.mir().basic_block_data(bb); + debug!("drop_flags_for_locs({:?})", data); + for i in 0..(data.statements.len()+1) { + debug!("drop_flag_for_locs: stmt {}", i); + let mut allow_initializations = true; + if i == data.statements.len() { + match data.terminator().kind { + TerminatorKind::Drop { .. } => { + // drop elaboration should handle that by itself + continue + } + TerminatorKind::DropAndReplace { .. } => { + // this contains the consume of the source and + // the initialization of the destination. We + // only want the latter + assert!(self.patch.is_patched(bb)); + allow_initializations = false; + } + _ => { + assert!(!self.patch.is_patched(bb)); + } + } + } + let loc = Location { block: bb, index: i }; + super::drop_flag_effects_for_location( + self.tcx(), self.mir(), self.move_data(), loc, |path, ds| { + if ds == DropFlagState::Dead || allow_initializations { + self.set_drop_flag(loc, path, ds) + } + } + ) + } + + // There may be a critical edge after this call, + // so mark the return as initialized *before* the + // call. + if let TerminatorKind::Call { + destination: Some((ref lv, _)), cleanup: None, .. + } = data.terminator().kind { + assert!(!self.patch.is_patched(bb)); + + let loc = Location { block: bb, index: data.statements.len() }; + let path = self.move_data().rev_lookup.find(lv); + on_all_children_bits( + self.tcx(), self.mir(), self.move_data(), path, + |child| self.set_drop_flag(loc, child, DropFlagState::Live) + ); + } + } + } + + fn drop_flags_for_drop<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + bb: BasicBlock) + { + let loc = self.patch.terminator_loc(self.mir(), bb); + on_all_children_bits( + self.tcx(), self.mir(), self.move_data(), c.path, + |child| self.set_drop_flag(loc, child, DropFlagState::Dead) + ); + } +} diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 38ebecf248ff7..7e1a196629f54 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -24,8 +24,10 @@ use rustc::session::Session; use rustc::ty::{self, TyCtxt}; mod abs_domain; +pub mod elaborate_drops; mod dataflow; mod gather_moves; +mod patch; // mod graphviz; use self::dataflow::{BitDenotation}; @@ -34,7 +36,7 @@ use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults}; use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use self::dataflow::{DefinitelyInitializedLvals}; use self::gather_moves::{MoveData, MovePathIndex, Location}; -use self::gather_moves::{MovePathContent}; +use self::gather_moves::{MovePathContent, MovePathData}; fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option> { for attr in attrs { @@ -202,6 +204,37 @@ enum DropFlagState { Absent, // i.e. deinitialized or "moved" } +impl DropFlagState { + fn value(self) -> bool { + match self { + DropFlagState::Live => true, + DropFlagState::Dead => false + } + } +} + +fn move_path_children_matching<'tcx, F>(move_paths: &MovePathData<'tcx>, + path: MovePathIndex, + mut cond: F) + -> Option + where F: FnMut(&repr::LvalueProjection<'tcx>) -> bool +{ + let mut next_child = move_paths[path].first_child; + while let Some(child_index) = next_child { + match move_paths[child_index].content { + MovePathContent::Lvalue(repr::Lvalue::Projection(ref proj)) => { + if cond(proj) { + return Some(child_index) + } + } + _ => {} + } + next_child = move_paths[child_index].next_sibling; + } + + None +} + fn on_all_children_bits<'a, 'tcx, F>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, diff --git a/src/librustc_borrowck/borrowck/mir/patch.rs b/src/librustc_borrowck/borrowck/mir/patch.rs new file mode 100644 index 0000000000000..b390c19af1a5b --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/patch.rs @@ -0,0 +1,184 @@ +// 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. + +use super::gather_moves::Location; +use rustc::ty::Ty; +use rustc::mir::repr::*; +use syntax::codemap::Span; + +use std::iter; +use std::u32; + +/// This struct represents a patch to MIR, which can add +/// new statements and basic blocks and patch over block +/// terminators. +pub struct MirPatch<'tcx> { + patch_map: Vec>>, + new_blocks: Vec>, + new_statements: Vec<(Location, StatementKind<'tcx>)>, + new_temps: Vec>, + resume_block: BasicBlock, + next_temp: u32, +} + +impl<'tcx> MirPatch<'tcx> { + pub fn new(mir: &Mir<'tcx>) -> Self { + let mut result = MirPatch { + patch_map: iter::repeat(None) + .take(mir.basic_blocks.len()).collect(), + new_blocks: vec![], + new_temps: vec![], + new_statements: vec![], + next_temp: mir.temp_decls.len() as u32, + resume_block: START_BLOCK + }; + + // make sure the MIR we create has a resume block. It is + // completely legal to convert jumps to the resume block + // to jumps to None, but we occasionally have to add + // instructions just before that. + + let mut resume_block = None; + let mut resume_stmt_block = None; + for block in mir.all_basic_blocks() { + let data = mir.basic_block_data(block); + if let TerminatorKind::Resume = data.terminator().kind { + if data.statements.len() > 0 { + resume_stmt_block = Some(block); + } else { + resume_block = Some(block); + } + break + } + } + let resume_block = resume_block.unwrap_or_else(|| { + result.new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + span: mir.span, + scope: ScopeId::new(0), + kind: TerminatorKind::Resume + }), + is_cleanup: true + })}); + result.resume_block = resume_block; + if let Some(resume_stmt_block) = resume_stmt_block { + result.patch_terminator(resume_stmt_block, TerminatorKind::Goto { + target: resume_block + }); + } + result + } + + pub fn resume_block(&self) -> BasicBlock { + self.resume_block + } + + pub fn is_patched(&self, bb: BasicBlock) -> bool { + self.patch_map[bb.index()].is_some() + } + + pub fn terminator_loc(&self, mir: &Mir<'tcx>, bb: BasicBlock) -> Location { + let offset = match bb.index().checked_sub(mir.basic_blocks.len()) { + Some(index) => self.new_blocks[index].statements.len(), + None => mir.basic_block_data(bb).statements.len() + }; + Location { + block: bb, + index: offset + } + } + + pub fn new_temp(&mut self, ty: Ty<'tcx>) -> u32 { + let index = self.next_temp; + assert!(self.next_temp < u32::MAX); + self.next_temp += 1; + self.new_temps.push(TempDecl { ty: ty }); + index + } + + pub fn new_block(&mut self, data: BasicBlockData<'tcx>) -> BasicBlock { + let block = BasicBlock::new(self.patch_map.len()); + debug!("MirPatch: new_block: {:?}: {:?}", block, data); + self.new_blocks.push(data); + self.patch_map.push(None); + block + } + + pub fn patch_terminator(&mut self, block: BasicBlock, new: TerminatorKind<'tcx>) { + assert!(self.patch_map[block.index()].is_none()); + debug!("MirPatch: patch_terminator({:?}, {:?})", block, new); + self.patch_map[block.index()] = Some(new); + } + + pub fn add_statement(&mut self, loc: Location, stmt: StatementKind<'tcx>) { + debug!("MirPatch: add_statement({:?}, {:?})", loc, stmt); + self.new_statements.push((loc, stmt)); + } + + pub fn add_assign(&mut self, loc: Location, lv: Lvalue<'tcx>, rv: Rvalue<'tcx>) { + self.add_statement(loc, StatementKind::Assign(lv, rv)); + } + + pub fn apply(self, mir: &mut Mir<'tcx>) { + debug!("MirPatch: {:?} new temps, starting from index {}: {:?}", + self.new_temps.len(), mir.temp_decls.len(), self.new_temps); + debug!("MirPatch: {} new blocks, starting from index {}", + self.new_blocks.len(), mir.basic_blocks.len()); + mir.basic_blocks.extend(self.new_blocks); + mir.temp_decls.extend(self.new_temps); + for (src, patch) in self.patch_map.into_iter().enumerate() { + if let Some(patch) = patch { + debug!("MirPatch: patching block {:?}", src); + mir.basic_blocks[src].terminator_mut().kind = patch; + } + } + + let mut new_statements = self.new_statements; + new_statements.sort_by(|u,v| u.0.cmp(&v.0)); + + let mut delta = 0; + let mut last_bb = START_BLOCK; + for (mut loc, stmt) in new_statements { + if loc.block != last_bb { + delta = 0; + last_bb = loc.block; + } + debug!("MirPatch: adding statement {:?} at loc {:?}+{}", + stmt, loc, delta); + loc.index += delta; + let (span, scope) = Self::context_for_index( + mir.basic_block_data(loc.block), loc + ); + mir.basic_block_data_mut(loc.block).statements.insert( + loc.index, Statement { + span: span, + scope: scope, + kind: stmt + }); + delta += 1; + } + } + + pub fn context_for_index(data: &BasicBlockData, loc: Location) -> (Span, ScopeId) { + match data.statements.get(loc.index) { + Some(stmt) => (stmt.span, stmt.scope), + None => (data.terminator().span, data.terminator().scope) + } + } + + pub fn context_for_location(&self, mir: &Mir, loc: Location) -> (Span, ScopeId) { + let data = match loc.block.index().checked_sub(mir.basic_blocks.len()) { + Some(new) => &self.new_blocks[new], + None => mir.basic_block_data(loc.block) + }; + Self::context_for_index(data, loc) + } +} diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs index 819717628d62d..5acbb18a2ffee 100644 --- a/src/librustc_borrowck/borrowck/mod.rs +++ b/src/librustc_borrowck/borrowck/mod.rs @@ -18,6 +18,8 @@ pub use self::bckerr_code::*; pub use self::AliasableViolationKind::*; pub use self::MovedValueUseKind::*; +pub use self::mir::elaborate_drops::ElaborateDrops; + use self::InteriorKind::*; use rustc::dep_graph::DepNode; diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 9d7e05ed9fa86..cc694c59245f7 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -39,7 +39,7 @@ extern crate core; // for NonZero pub use borrowck::check_crate; pub use borrowck::build_borrowck_dataflow_data_for_fn; -pub use borrowck::{AnalysisData, BorrowckCtxt}; +pub use borrowck::{AnalysisData, BorrowckCtxt, ElaborateDrops}; // NB: This module needs to be declared first so diagnostics are // registered before they are used. diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 911626bd2c2cc..bfad281702fd0 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1033,6 +1033,10 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); passes.push_pass(box mir::transform::erase_regions::EraseRegions); passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges); + passes.push_pass(box borrowck::ElaborateDrops); + passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); + passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); + passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges); passes.run_passes(tcx, &mut mir_map); }); diff --git a/src/test/run-fail/issue-30380.rs b/src/test/run-fail/issue-30380.rs new file mode 100644 index 0000000000000..7bd9adcba9bd1 --- /dev/null +++ b/src/test/run-fail/issue-30380.rs @@ -0,0 +1,44 @@ +// 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. + +// check that panics in destructors during assignment do not leave +// destroyed values lying around for other destructors to observe. + +// error-pattern:panicking destructors ftw! + +struct Observer<'a>(&'a mut FilledOnDrop); + +struct FilledOnDrop(u32); +impl Drop for FilledOnDrop { + fn drop(&mut self) { + if self.0 == 0 { + // this is only set during the destructor - safe + // code should not be able to observe this. + self.0 = 0x1c1c1c1c; + panic!("panicking destructors ftw!"); + } + } +} + +impl<'a> Drop for Observer<'a> { + fn drop(&mut self) { + assert_eq!(self.0 .0, 1); + } +} + +fn foo(b: &mut Observer) { + *b.0 = FilledOnDrop(1); +} + +fn main() { + let mut bomb = FilledOnDrop(0); + let mut observer = Observer(&mut bomb); + foo(&mut observer); +} diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs new file mode 100644 index 0000000000000..48e7b7ca57696 --- /dev/null +++ b/src/test/run-pass/dynamic-drop.rs @@ -0,0 +1,100 @@ +// 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. + +use std::cell::RefCell; + +struct Allocator { + data: RefCell>, +} + +impl Drop for Allocator { + fn drop(&mut self) { + let data = self.data.borrow(); + if data.iter().any(|d| *d) { + panic!("missing free: {:?}", data); + } + } +} + +impl Allocator { + fn new() -> Self { Allocator { data: RefCell::new(vec![]) } } + fn alloc(&self) -> Ptr { + let mut data = self.data.borrow_mut(); + let addr = data.len(); + data.push(true); + Ptr(addr, self) + } +} + +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 at index {:?}", self.0) + } + ref mut d => *d = false + } + } +} + +fn dynamic_init(a: &Allocator, c: bool) { + let _x; + if c { + _x = Some(a.alloc()); + } +} + +fn dynamic_drop(a: &Allocator, c: bool) -> Option { + let x = a.alloc(); + if c { + Some(x) + } else { + None + } +} + +fn assignment2(a: &Allocator, c0: bool, c1: bool) { + let mut _v = a.alloc(); + let mut _w = a.alloc(); + if c0 { + drop(_v); + } + _v = _w; + if c1 { + _w = a.alloc(); + } +} + +fn assignment1(a: &Allocator, c0: bool) { + let mut _v = a.alloc(); + let mut _w = a.alloc(); + if c0 { + drop(_v); + } + _v = _w; +} + + +fn main() { + let a = Allocator::new(); + dynamic_init(&a, false); + dynamic_init(&a, true); + dynamic_drop(&a, false); + dynamic_drop(&a, true); + + assignment2(&a, false, false); + assignment2(&a, false, true); + assignment2(&a, true, false); + assignment2(&a, true, true); + + assignment1(&a, false); + assignment1(&a, true); +} From 8d6d646203254fbd3bddfd5f40b13140ef12211a Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Fri, 27 May 2016 15:07:08 +0300 Subject: [PATCH 03/11] address review comments --- .../borrowck/mir/dataflow/mod.rs | 2 +- .../borrowck/mir/elaborate_drops.rs | 156 +++++++++--------- src/librustc_borrowck/borrowck/mir/mod.rs | 4 +- 3 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 113d3ff8512b1..293d2863733f7 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -201,7 +201,7 @@ impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> pub struct DataflowResults(DataflowState) where O: BitDenotation; impl DataflowResults { - pub fn sets(&self) -> &AllSets { + pub fn sets(&self) -> &AllSets { &self.0.sets } } diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index e299d47aee61c..6e5303626f116 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -8,12 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use bitslice::BitSlice; +use indexed_set::IdxSetBuf; use super::gather_moves::{MoveData, MovePathIndex, MovePathContent, Location}; use super::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use super::dataflow::{DataflowResults}; use super::{drop_flag_effects_for_location, on_all_children_bits}; -use super::{DropFlagState}; +use super::{DropFlagState, MoveDataParamEnv}; use super::patch::MirPatch; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{Subst, Substs, VecPerParamSpace}; @@ -44,23 +44,26 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { let move_data = MoveData::gather_moves(mir, tcx); let elaborate_patch = { let mir = &*mir; - let ((_, _, move_data), flow_inits) = - super::do_dataflow(tcx, mir, id, &[], (tcx, mir, move_data), - MaybeInitializedLvals::default()); - let ((_, _, move_data), flow_uninits) = - super::do_dataflow(tcx, mir, id, &[], (tcx, mir, move_data), - MaybeUninitializedLvals::default()); - - match (tcx, mir, move_data) { - ref ctxt => ElaborateDropsCtxt { - ctxt: ctxt, - param_env: ¶m_env, - flow_inits: flow_inits, - flow_uninits: flow_uninits, - drop_flags: FnvHashMap(), - patch: MirPatch::new(mir), - }.elaborate() - } + let env = MoveDataParamEnv { + move_data: move_data, + param_env: param_env + }; + let flow_inits = + super::do_dataflow(tcx, mir, id, &[], &env, + MaybeInitializedLvals::new(tcx, mir)); + let flow_uninits = + super::do_dataflow(tcx, mir, id, &[], &env, + MaybeUninitializedLvals::new(tcx, mir)); + + ElaborateDropsCtxt { + tcx: tcx, + mir: mir, + env: &env, + flow_inits: flow_inits, + flow_uninits: flow_uninits, + drop_flags: FnvHashMap(), + patch: MirPatch::new(mir), + }.elaborate() }; pretty::dump_mir(tcx, "elaborate_drops", &0, src, mir, None); elaborate_patch.apply(mir); @@ -71,35 +74,35 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops { impl Pass for ElaborateDrops {} struct InitializationData { - live: Vec, - dead: Vec + live: IdxSetBuf, + dead: IdxSetBuf } impl InitializationData { fn apply_location<'a,'tcx>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - move_data: &MoveData<'tcx>, + env: &MoveDataParamEnv<'tcx>, loc: Location) { - drop_flag_effects_for_location(tcx, mir, move_data, loc, |path, df| { + drop_flag_effects_for_location(tcx, mir, env, loc, |path, df| { debug!("at location {:?}: setting {:?} to {:?}", loc, path, df); match df { - DropFlagState::Live => { - self.live.set_bit(path.idx()); - self.dead.clear_bit(path.idx()); + DropFlagState::Present => { + self.live.add(&path); + self.dead.remove(&path); } - DropFlagState::Dead => { - self.dead.set_bit(path.idx()); - self.live.clear_bit(path.idx()); + DropFlagState::Absent => { + self.dead.add(&path); + self.live.remove(&path); } } }); } fn state(&self, path: MovePathIndex) -> (bool, bool) { - (self.live.get_bit(path.idx()), self.dead.get_bit(path.idx())) + (self.live.contains(&path), self.dead.contains(&path)) } } @@ -110,8 +113,9 @@ impl fmt::Debug for InitializationData { } struct ElaborateDropsCtxt<'a, 'tcx: 'a> { - ctxt: &'a (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>), - param_env: &'a ty::ParameterEnvironment<'tcx>, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, + env: &'a MoveDataParamEnv<'tcx>, flow_inits: DataflowResults>, flow_uninits: DataflowResults>, drop_flags: FnvHashMap, @@ -133,9 +137,10 @@ struct DropCtxt<'a, 'tcx: 'a> { } impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { - fn tcx(&self) -> TyCtxt<'b, 'tcx, 'tcx> { self.ctxt.0 } - fn mir(&self) -> &'b Mir<'tcx> { self.ctxt.1 } - fn move_data(&self) -> &'b MoveData<'tcx> { &self.ctxt.2 } + fn move_data(&self) -> &'b MoveData<'tcx> { &self.env.move_data } + fn param_env(&self) -> &'b ty::ParameterEnvironment<'tcx> { + &self.env.param_env + } fn initialization_data_at(&self, loc: Location) -> InitializationData { let mut data = InitializationData { @@ -145,14 +150,14 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { .to_owned(), }; for stmt in 0..loc.index { - data.apply_location(self.ctxt.0, self.ctxt.1, &self.ctxt.2, + data.apply_location(self.tcx, self.mir, self.env, Location { block: loc.block, index: stmt }); } data } fn create_drop_flag(&mut self, index: MovePathIndex) { - let tcx = self.tcx(); + let tcx = self.tcx; let patch = &mut self.patch; self.drop_flags.entry(index).or_insert_with(|| { patch.new_temp(tcx.types.bool) @@ -183,11 +188,10 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { { match self.move_data().move_paths[path].content { MovePathContent::Lvalue(ref lvalue) => { - let ty = self.mir().lvalue_ty(self.tcx(), lvalue) - .to_ty(self.tcx()); + let ty = self.mir.lvalue_ty(self.tcx, lvalue).to_ty(self.tcx); debug!("path_needs_drop({:?}, {:?} : {:?})", path, lvalue, ty); - self.tcx().type_needs_drop_given_env(ty, &self.param_env) + self.tcx.type_needs_drop_given_env(ty, self.param_env()) } _ => false } @@ -210,7 +214,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } fn lvalue_contents_are_tracked(&self, lv: &Lvalue<'tcx>) -> bool { - let ty = self.mir().lvalue_ty(self.tcx(), lv).to_ty(self.tcx()); + let ty = self.mir.lvalue_ty(self.tcx, lv).to_ty(self.tcx); match ty.sty { ty::TyArray(..) | ty::TySlice(..) | ty::TyRef(..) | ty::TyRawPtr(..) => { false @@ -221,8 +225,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn collect_drop_flags(&mut self) { - for bb in self.mir().all_basic_blocks() { - let data = self.mir().basic_block_data(bb); + for bb in self.mir.all_basic_blocks() { + let data = self.mir.basic_block_data(bb); let terminator = data.terminator(); let location = match terminator.kind { TerminatorKind::Drop { ref location, .. } | @@ -243,7 +247,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { debug!("collect_drop_flags: {:?}, lv {:?} (index {:?})", bb, location, path); - on_all_children_bits(self.tcx(), self.mir(), self.move_data(), path, |child| { + on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { if self.path_needs_drop(child) { let (maybe_live, maybe_dead) = init_data.state(child); debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", @@ -258,8 +262,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn elaborate_drops(&mut self) { - for bb in self.mir().all_basic_blocks() { - let data = self.mir().basic_block_data(bb); + for bb in self.mir.all_basic_blocks() { + let data = self.mir.basic_block_data(bb); let loc = Location { block: bb, index: data.statements.len() }; let terminator = data.terminator(); @@ -320,7 +324,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { unwind: Option) { let bb = loc.block; - let data = self.mir().basic_block_data(bb); + let data = self.mir.basic_block_data(bb); let terminator = data.terminator(); let unwind = Some(unwind.unwrap_or_else(|| { @@ -354,12 +358,12 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { succ: target, unwind: unwind }, bb); - on_all_children_bits(self.tcx(), self.mir(), self.move_data(), path, |child| { + on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { self.set_drop_flag(Location { block: target, index: 0 }, - child, DropFlagState::Live); + child, DropFlagState::Present); if let Some(unwind) = unwind { self.set_drop_flag(Location { block: unwind, index: 0 }, - child, DropFlagState::Live); + child, DropFlagState::Present); } }); } @@ -397,7 +401,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let mut some_dead = false; let mut children_count = 0; on_all_children_bits( - self.tcx(), self.mir(), self.move_data(), + self.tcx, self.mir, self.move_data(), c.path, |child| { if self.path_needs_drop(child) { let (live, dead) = c.init_data.state(child); @@ -463,9 +467,9 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }); let field_ty = - self.tcx().normalize_associated_type_in_env( - &f.ty(self.tcx(), substs), - &self.param_env + self.tcx.normalize_associated_type_in_env( + &f.ty(self.tcx, substs), + self.param_env() ); (base_lv.clone().field(Field::new(i), field_ty), subpath) }).collect() @@ -704,7 +708,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { /// This creates a "drop ladder" that drops the needed fields of the /// ADT, both in the success case or if one of the destructors fail. fn open_drop<'a>(&mut self, c: &DropCtxt<'a, 'tcx>) -> BasicBlock { - let ty = self.mir().lvalue_ty(self.tcx(), c.lvalue).to_ty(self.tcx()); + let ty = self.mir.lvalue_ty(self.tcx, c.lvalue).to_ty(self.tcx); match ty.sty { ty::TyStruct(def, substs) | ty::TyEnum(def, substs) => { self.open_drop_for_adt(c, def, substs) @@ -741,7 +745,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.set_drop_flag( Location { block: drop_block, index: 0 }, c.path, - DropFlagState::Dead + DropFlagState::Absent ); } @@ -859,7 +863,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }); } - let tcx = self.tcx(); + let tcx = self.tcx; let unit_temp = Lvalue::Temp(self.patch.new_temp(tcx.mk_nil())); let free_func = tcx.lang_items.require(lang_items::BoxFreeFnLangItem) .unwrap_or_else(|e| tcx.sess.fatal(&e)); @@ -896,12 +900,12 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { // dataflow can create unneeded children in some cases // - be sure to ignore them. - let ty = self.mir().lvalue_ty(self.tcx(), c.lvalue).to_ty(self.tcx()); + let ty = self.mir.lvalue_ty(self.tcx, c.lvalue).to_ty(self.tcx); match ty.sty { ty::TyStruct(def, _) | ty::TyEnum(def, _) => { if def.has_dtor() { - self.tcx().sess.span_warn( + self.tcx.sess.span_warn( c.span, &format!("dataflow bug??? moving out of type with dtor {:?}", c)); @@ -917,14 +921,14 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn constant_bool(&self, span: Span, val: bool) -> Rvalue<'tcx> { Rvalue::Use(Operand::Constant(Constant { span: span, - ty: self.tcx().types.bool, + ty: self.tcx.types.bool, literal: Literal::Value { value: ConstVal::Bool(val) } })) } fn set_drop_flag(&mut self, loc: Location, path: MovePathIndex, val: DropFlagState) { if let Some(&flag) = self.drop_flags.get(&path) { - let span = self.patch.context_for_location(self.mir(), loc).0; + let span = self.patch.context_for_location(self.mir, loc).0; let val = self.constant_bool(span, val.value()); self.patch.add_assign(loc, Lvalue::Temp(flag), val); } @@ -932,7 +936,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn drop_flags_on_init(&mut self) { let loc = Location { block: START_BLOCK, index: 0 }; - let span = self.patch.context_for_location(self.mir(), loc).0; + let span = self.patch.context_for_location(self.mir, loc).0; let false_ = self.constant_bool(span, false); for flag in self.drop_flags.values() { self.patch.add_assign(loc, Lvalue::Temp(*flag), false_.clone()); @@ -940,8 +944,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } fn drop_flags_for_fn_rets(&mut self) { - for bb in self.mir().all_basic_blocks() { - let data = self.mir().basic_block_data(bb); + for bb in self.mir.all_basic_blocks() { + let data = self.mir.basic_block_data(bb); if let TerminatorKind::Call { destination: Some((ref lv, tgt)), cleanup: Some(_), .. } = data.terminator().kind { @@ -950,8 +954,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let loc = Location { block: tgt, index: 0 }; let path = self.move_data().rev_lookup.find(lv); on_all_children_bits( - self.tcx(), self.mir(), self.move_data(), path, - |child| self.set_drop_flag(loc, child, DropFlagState::Live) + self.tcx, self.mir, self.move_data(), path, + |child| self.set_drop_flag(loc, child, DropFlagState::Present) ); } } @@ -960,7 +964,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn drop_flags_for_args(&mut self) { let loc = Location { block: START_BLOCK, index: 0 }; super::drop_flag_effects_for_function_entry( - self.tcx(), self.mir(), self.move_data(), |path, ds| { + self.tcx, self.mir, self.env, |path, ds| { self.set_drop_flag(loc, path, ds); } ) @@ -973,8 +977,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { // drop flags by themselves, to avoid the drop flags being // clobbered before they are read. - for bb in self.mir().all_basic_blocks() { - let data = self.mir().basic_block_data(bb); + for bb in self.mir.all_basic_blocks() { + let data = self.mir.basic_block_data(bb); debug!("drop_flags_for_locs({:?})", data); for i in 0..(data.statements.len()+1) { debug!("drop_flag_for_locs: stmt {}", i); @@ -999,8 +1003,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } let loc = Location { block: bb, index: i }; super::drop_flag_effects_for_location( - self.tcx(), self.mir(), self.move_data(), loc, |path, ds| { - if ds == DropFlagState::Dead || allow_initializations { + self.tcx, self.mir, self.env, loc, |path, ds| { + if ds == DropFlagState::Absent || allow_initializations { self.set_drop_flag(loc, path, ds) } } @@ -1018,8 +1022,8 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let loc = Location { block: bb, index: data.statements.len() }; let path = self.move_data().rev_lookup.find(lv); on_all_children_bits( - self.tcx(), self.mir(), self.move_data(), path, - |child| self.set_drop_flag(loc, child, DropFlagState::Live) + self.tcx, self.mir, self.move_data(), path, + |child| self.set_drop_flag(loc, child, DropFlagState::Present) ); } } @@ -1029,10 +1033,10 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { c: &DropCtxt<'a, 'tcx>, bb: BasicBlock) { - let loc = self.patch.terminator_loc(self.mir(), bb); + let loc = self.patch.terminator_loc(self.mir, bb); on_all_children_bits( - self.tcx(), self.mir(), self.move_data(), c.path, - |child| self.set_drop_flag(loc, child, DropFlagState::Dead) + self.tcx, self.mir, self.move_data(), c.path, + |child| self.set_drop_flag(loc, child, DropFlagState::Absent) ); } } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 7e1a196629f54..007cde156f40f 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -207,8 +207,8 @@ enum DropFlagState { impl DropFlagState { fn value(self) -> bool { match self { - DropFlagState::Live => true, - DropFlagState::Dead => false + DropFlagState::Present => true, + DropFlagState::Absent => false } } } From 4fff19528b80e4d2600c8866937bb70e9482e13b Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 28 May 2016 01:10:16 +0300 Subject: [PATCH 04/11] remove filling on drop --- src/librustc_trans/mir/block.rs | 22 +--------------------- src/librustc_trans/mir/drop.rs | 27 --------------------------- src/librustc_trans/mir/lvalue.rs | 4 ---- src/librustc_trans/mir/mod.rs | 1 - src/librustc_trans/mir/operand.rs | 28 +--------------------------- src/librustc_trans/mir/rvalue.rs | 26 ++++---------------------- 6 files changed, 6 insertions(+), 102 deletions(-) delete mode 100644 src/librustc_trans/mir/drop.rs diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index fb93e487f3bb2..a917327b0523a 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -26,7 +26,7 @@ use glue; use type_::Type; use rustc_data_structures::fnv::FnvHashMap; -use super::{MirContext, TempRef, drop}; +use super::{MirContext, TempRef}; use super::constant::Const; use super::lvalue::{LvalueRef, load_fat_ptr}; use super::operand::OperandRef; @@ -168,11 +168,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { cleanup_bundle.as_ref()); self.bcx(target).at_start(|bcx| { debug_loc.apply_to_bcx(bcx); - drop::drop_fill(bcx, lvalue.llval, ty) }); } else { bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref()); - drop::drop_fill(&bcx, lvalue.llval, ty); funclet_br(bcx, self.llblock(target)); } } @@ -215,7 +213,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llptr = self.trans_operand(&bcx, &args[0]).immediate(); let val = self.trans_operand(&bcx, &args[1]); self.store_operand(&bcx, llptr, val); - self.set_operand_dropped(&bcx, &args[1]); funclet_br(bcx, self.llblock(target)); return; } @@ -226,7 +223,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { this.trans_transmute(&bcx, &args[0], dest); }); - self.set_operand_dropped(&bcx, &args[0]); funclet_br(bcx, self.llblock(target)); return; } @@ -332,9 +328,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } if let Some((_, target)) = *destination { - for op in args { - self.set_operand_dropped(&bcx, op); - } funclet_br(bcx, self.llblock(target)); } else { // trans_intrinsic_call already used Unreachable. @@ -363,13 +356,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { cleanup_bundle.as_ref()); fn_ty.apply_attrs_callsite(invokeret); - landingpad.at_start(|bcx| { - debug_loc.apply_to_bcx(bcx); - for op in args { - self.set_operand_dropped(bcx, op); - } - }); - if destination.is_some() { let ret_bcx = ret_bcx.build(); ret_bcx.at_start(|ret_bcx| { @@ -379,9 +365,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ty: sig.output.unwrap() }; self.store_return(&ret_bcx, ret_dest, fn_ty.ret, op); - for op in args { - self.set_operand_dropped(&ret_bcx, op); - } }); } } else { @@ -393,9 +376,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ty: sig.output.unwrap() }; self.store_return(&bcx, ret_dest, fn_ty.ret, op); - for op in args { - self.set_operand_dropped(&bcx, op); - } funclet_br(bcx, self.llblock(target)); } else { // no need to drop args, because the call never returns diff --git a/src/librustc_trans/mir/drop.rs b/src/librustc_trans/mir/drop.rs deleted file mode 100644 index 623cd5a6f8cc9..0000000000000 --- a/src/librustc_trans/mir/drop.rs +++ /dev/null @@ -1,27 +0,0 @@ -// 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::ty::Ty; -use adt; -use base; -use common::{self, BlockAndBuilder}; -use machine; -use type_of; -use type_::Type; - -pub fn drop_fill<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, value: ValueRef, ty: Ty<'tcx>) { - let llty = type_of::type_of(bcx.ccx(), ty); - let llptr = bcx.pointercast(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(&bcx, llptr, filling, size, align, false); -} diff --git a/src/librustc_trans/mir/lvalue.rs b/src/librustc_trans/mir/lvalue.rs index b39a6ac1ce381..bc79482666c92 100644 --- a/src/librustc_trans/mir/lvalue.rs +++ b/src/librustc_trans/mir/lvalue.rs @@ -20,7 +20,6 @@ use common::{self, BlockAndBuilder, CrateContext, C_uint, C_undef}; use consts; use machine; use type_of::type_of; -use mir::drop; use Disr; use std::ptr; @@ -51,9 +50,6 @@ impl<'tcx> LvalueRef<'tcx> { { assert!(!ty.has_erasable_regions()); let lltemp = bcx.with_block(|bcx| base::alloc_ty(bcx, ty, name)); - if bcx.fcx().type_needs_drop(ty) { - drop::drop_fill(bcx, lltemp, ty); - } LvalueRef::new_sized(lltemp, LvalueTy::from_ty(ty)) } diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index ffc14b4468b5b..3ff304758116c 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -431,7 +431,6 @@ fn arg_value_refs<'bcx, 'tcx>(bcx: &BlockAndBuilder<'bcx, 'tcx>, mod analyze; mod block; mod constant; -mod drop; mod lvalue; mod operand; mod rvalue; diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs index fc726a3474f7e..107ec1159f010 100644 --- a/src/librustc_trans/mir/operand.rs +++ b/src/librustc_trans/mir/operand.rs @@ -15,12 +15,11 @@ use base; use common::{self, Block, BlockAndBuilder}; use datum; use value::Value; -use glue; use std::fmt; use super::lvalue::load_fat_ptr; -use super::{MirContext, TempRef, drop}; +use super::{MirContext, TempRef}; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -179,29 +178,4 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } - - pub fn set_operand_dropped(&mut self, - bcx: &BlockAndBuilder<'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] { - // All lvalues which have an associated drop are promoted to an alloca - // beforehand. If this is an operand, it is safe to say this is never - // dropped and there’s no reason for us to zero this out at all. - return - } - } - let lvalue = self.trans_lvalue(bcx, lvalue); - let ty = lvalue.ty.to_ty(bcx.tcx()); - if !glue::type_needs_drop(bcx.tcx(), ty) { - return - } else { - drop::drop_fill(bcx, lvalue.llval, ty); - } - } - } - } } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 5945e8813a48d..6d141862ac3ff 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -25,7 +25,6 @@ use type_of; use tvec; use value::Value; use Disr; -use glue; use super::MirContext; use super::operand::{OperandRef, OperandValue}; @@ -48,7 +47,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // 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?) self.store_operand(&bcx, dest.llval, tr_operand); - self.set_operand_dropped(&bcx, operand); bcx } @@ -92,7 +90,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } }); - self.set_operand_dropped(&bcx, source); bcx } @@ -107,7 +104,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { block }) }); - self.set_operand_dropped(&bcx, elem); bcx } @@ -128,7 +124,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { val, disr, i); self.store_operand(&bcx, lldest_i, op); } - self.set_operand_dropped(&bcx, operand); } }, _ => { @@ -167,7 +162,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let dest = bcx.gepi(dest.llval, &[0, i]); self.store_operand(&bcx, dest, op); } - self.set_operand_dropped(&bcx, operand); } } } @@ -209,9 +203,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { asm::trans_inline_asm(bcx, asm, outputs, input_vals); }); - for input in inputs { - self.set_operand_dropped(&bcx, input); - } bcx } @@ -269,7 +260,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // &'a fmt::Debug+Send => &'a fmt::Debug, // So we need to pointercast the base to ensure // the types match up. - self.set_operand_dropped(&bcx, source); let llcast_ty = type_of::fat_ptr_base_ty(bcx.ccx(), cast_ty); let lldata = bcx.pointercast(lldata, llcast_ty); OperandValue::FatPtr(lldata, llextra) @@ -280,7 +270,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::unsize_thin_ptr(bcx, lldata, operand.ty, cast_ty) }); - self.set_operand_dropped(&bcx, source); OperandValue::FatPtr(lldata, llextra) } OperandValue::Ref(_) => { @@ -569,8 +558,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } -pub fn rvalue_creates_operand<'bcx, 'tcx>(mir: &mir::Mir<'tcx>, - bcx: &BlockAndBuilder<'bcx, 'tcx>, +pub fn rvalue_creates_operand<'bcx, 'tcx>(_mir: &mir::Mir<'tcx>, + _bcx: &BlockAndBuilder<'bcx, 'tcx>, rvalue: &mir::Rvalue<'tcx>) -> bool { match *rvalue { mir::Rvalue::Ref(..) | @@ -578,21 +567,14 @@ pub fn rvalue_creates_operand<'bcx, 'tcx>(mir: &mir::Mir<'tcx>, mir::Rvalue::Cast(..) | // (*) mir::Rvalue::BinaryOp(..) | mir::Rvalue::UnaryOp(..) | - mir::Rvalue::Box(..) => + mir::Rvalue::Box(..) | + mir::Rvalue::Use(..) => true, mir::Rvalue::Repeat(..) | mir::Rvalue::Aggregate(..) | mir::Rvalue::Slice { .. } | mir::Rvalue::InlineAsm { .. } => false, - mir::Rvalue::Use(ref operand) => { - let ty = mir.operand_ty(bcx.tcx(), operand); - let ty = bcx.monomorphize(&ty); - // Types that don't need dropping can just be an operand, - // this allows temporary lvalues, used as rvalues, to - // avoid a stack slot when it's unnecessary - !glue::type_needs_drop(bcx.tcx(), ty) - } } // (*) this is only true if the type is suitable From f6068ea84788743079963471a8415884af2a6e56 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 29 May 2016 19:27:05 +0300 Subject: [PATCH 05/11] fix ICEs with RUST_LOG --- src/librustc/middle/cstore.rs | 2 ++ src/librustc/mir/repr.rs | 7 +++-- src/librustc/ty/item_path.rs | 4 ++- src/librustc/ty/mod.rs | 12 ++++++++ src/librustc/util/ppaux.rs | 44 ++++++++++++++++-------------- src/librustc_metadata/csearch.rs | 5 ++++ src/librustc_metadata/decoder.rs | 22 +++++++++++---- src/librustc_trans/monomorphize.rs | 2 +- 8 files changed, 66 insertions(+), 32 deletions(-) diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs index 3ede60beb7443..d85ea96146227 100644 --- a/src/librustc/middle/cstore.rs +++ b/src/librustc/middle/cstore.rs @@ -163,6 +163,7 @@ pub trait CrateStore<'tcx> { -> ty::TypeScheme<'tcx>; fn visible_parent_map<'a>(&'a self) -> ::std::cell::RefMut<'a, DefIdMap>; fn item_name(&self, def: DefId) -> ast::Name; + fn opt_item_name(&self, def: DefId) -> Option; fn item_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::GenericPredicates<'tcx>; fn item_super_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) @@ -345,6 +346,7 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore { bug!("visible_parent_map") } fn item_name(&self, def: DefId) -> ast::Name { bug!("item_name") } + fn opt_item_name(&self, def: DefId) -> Option { bug!("opt_item_name") } fn item_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) -> ty::GenericPredicates<'tcx> { bug!("item_predicates") } fn item_super_predicates<'a>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def: DefId) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index db4c0c1e9eb77..1cd837e4853b0 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -942,7 +942,7 @@ impl<'tcx> Debug for Rvalue<'tcx> { ppaux::parameterized(fmt, substs, variant_def.did, ppaux::Ns::Value, &[], |tcx| { - tcx.lookup_item_type(variant_def.did).generics + Some(tcx.lookup_item_type(variant_def.did).generics) })?; match variant_def.kind() { @@ -1034,8 +1034,9 @@ impl<'tcx> Debug for Literal<'tcx> { use self::Literal::*; match *self { Item { def_id, substs } => { - ppaux::parameterized(fmt, substs, def_id, ppaux::Ns::Value, &[], - |tcx| tcx.lookup_item_type(def_id).generics) + ppaux::parameterized( + fmt, substs, def_id, ppaux::Ns::Value, &[], + |tcx| Some(tcx.lookup_item_type(def_id).generics)) } Value { ref value } => { write!(fmt, "const ")?; diff --git a/src/librustc/ty/item_path.rs b/src/librustc/ty/item_path.rs index ee9983038b162..74c05feb6d16a 100644 --- a/src/librustc/ty/item_path.rs +++ b/src/librustc/ty/item_path.rs @@ -13,6 +13,7 @@ use middle::cstore::LOCAL_CRATE; use hir::def_id::{DefId, CRATE_DEF_INDEX}; use ty::{self, Ty, TyCtxt}; use syntax::ast; +use syntax::parse::token; use std::cell::Cell; @@ -138,7 +139,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { } } - cur_path.push(self.sess.cstore.item_name(cur_def)); + cur_path.push(self.sess.cstore.opt_item_name(cur_def).unwrap_or_else(|| + token::intern(""))); match visible_parent_map.get(&cur_def) { Some(&def) => cur_def = def, None => return false, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 24f0671ce6184..8f42ca2abb92f 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2480,6 +2480,18 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { || self.sess.cstore.item_type(self.global_tcx(), did)) } + pub fn opt_lookup_item_type(self, did: DefId) -> Option> { + if let Some(scheme) = self.tcache.borrow_mut().get(&did) { + return Some(scheme.clone()); + } + + if did.krate == LOCAL_CRATE { + None + } else { + Some(self.sess.cstore.item_type(self.global_tcx(), did)) + } + } + /// Given the did of a trait, returns its canonical trait ref. pub fn lookup_trait_def(self, did: DefId) -> &'gcx TraitDef<'gcx> { lookup_locally_or_in_crate_store( diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index 1a802064b6127..bbee7afce7183 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -69,15 +69,12 @@ pub enum Ns { Value } -fn number_of_supplied_defaults<'a, 'gcx, 'tcx, GG>(tcx: TyCtxt<'a, 'gcx, 'tcx>, - substs: &subst::Substs, - space: subst::ParamSpace, - get_generics: GG) - -> usize - where GG: FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> ty::Generics<'tcx> +fn number_of_supplied_defaults<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>, + substs: &subst::Substs, + space: subst::ParamSpace, + generics: ty::Generics<'tcx>) + -> usize { - let generics = get_generics(tcx); - let has_self = substs.self_ty().is_some(); let ty_params = generics.types.get_slice(space); let tps = substs.types.get_slice(space); @@ -115,7 +112,8 @@ pub fn parameterized(f: &mut fmt::Formatter, projections: &[ty::ProjectionPredicate], get_generics: GG) -> fmt::Result - where GG: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) -> ty::Generics<'tcx> + where GG: for<'a, 'gcx, 'tcx> FnOnce(TyCtxt<'a, 'gcx, 'tcx>) + -> Option> { if let (Ns::Value, Some(self_ty)) = (ns, substs.self_ty()) { write!(f, "<{} as ", self_ty)?; @@ -176,13 +174,12 @@ pub fn parameterized(f: &mut fmt::Formatter, let num_supplied_defaults = if verbose { 0 } else { - // It is important to execute this conditionally, only if -Z - // verbose is false. Otherwise, debug logs can sometimes cause - // ICEs trying to fetch the generics early in the pipeline. This - // is kind of a hacky workaround in that -Z verbose is required to - // avoid those ICEs. ty::tls::with(|tcx| { - number_of_supplied_defaults(tcx, substs, subst::TypeSpace, get_generics) + if let Some(generics) = get_generics(tcx) { + number_of_supplied_defaults(tcx, substs, subst::TypeSpace, generics) + } else { + 0 + } }) }; @@ -310,7 +307,7 @@ impl<'tcx> fmt::Display for TraitAndProjections<'tcx> { trait_ref.def_id, Ns::Type, projection_bounds, - |tcx| tcx.lookup_trait_def(trait_ref.def_id).generics.clone()) + |tcx| Some(tcx.lookup_trait_def(trait_ref.def_id).generics.clone())) } } @@ -811,7 +808,7 @@ impl fmt::Display for ty::Binder> impl<'tcx> fmt::Display for ty::TraitRef<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { parameterized(f, self.substs, self.def_id, Ns::Type, &[], - |tcx| tcx.lookup_trait_def(self.def_id).generics.clone()) + |tcx| Some(tcx.lookup_trait_def(self.def_id).generics.clone())) } } @@ -863,8 +860,9 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> { } write!(f, "{} {{", bare_fn.sig.0)?; - parameterized(f, substs, def_id, Ns::Value, &[], - |tcx| tcx.lookup_item_type(def_id).generics)?; + parameterized( + f, substs, def_id, Ns::Value, &[], + |tcx| tcx.opt_lookup_item_type(def_id).map(|t| t.generics))?; write!(f, "}}") } TyFnPtr(ref bare_fn) => { @@ -887,8 +885,12 @@ impl<'tcx> fmt::Display for ty::TypeVariants<'tcx> { !tcx.tcache.borrow().contains_key(&def.did) { write!(f, "{}<..>", tcx.item_path_str(def.did)) } else { - parameterized(f, substs, def.did, Ns::Type, &[], - |tcx| tcx.lookup_item_type(def.did).generics) + parameterized( + f, substs, def.did, Ns::Type, &[], + |tcx| { + tcx.opt_lookup_item_type(def.did). + map(|t| t.generics) + }) } }) } diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs index 5d42f8c1d6f6a..3134a3844bc05 100644 --- a/src/librustc_metadata/csearch.rs +++ b/src/librustc_metadata/csearch.rs @@ -142,6 +142,11 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore { decoder::get_item_name(&self.intr, &cdata, def.index) } + fn opt_item_name(&self, def: DefId) -> Option { + self.dep_graph.read(DepNode::MetaData(def)); + let cdata = self.get_crate_data(def.krate); + decoder::maybe_get_item_name(&self.intr, &cdata, def.index) + } fn inherent_implementations_for_type(&self, def_id: DefId) -> Vec { diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d1153fe2d0603..d97ecebc476e9 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -285,12 +285,17 @@ fn item_trait_ref<'a, 'tcx>(doc: rbml::Doc, tcx: TyCtxt<'a, 'tcx, 'tcx>, cdata: } fn item_name(intr: &IdentInterner, item: rbml::Doc) -> ast::Name { - let name = reader::get_doc(item, tag_paths_data_name); - let string = name.as_str_slice(); - match intr.find(string) { - None => token::intern(string), - Some(val) => val, - } + maybe_item_name(intr, item).expect("no item in item_name") +} + +fn maybe_item_name(intr: &IdentInterner, item: rbml::Doc) -> Option { + reader::maybe_get_doc(item, tag_paths_data_name).map(|name| { + let string = name.as_str_slice(); + match intr.find(string) { + None => token::intern(string), + Some(val) => val, + } + }) } fn family_to_variant_kind<'tcx>(family: Family) -> Option { @@ -792,6 +797,11 @@ pub fn get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) -> ast::Nam item_name(intr, cdata.lookup_item(id)) } +pub fn maybe_get_item_name(intr: &IdentInterner, cdata: Cmd, id: DefIndex) + -> Option { + maybe_item_name(intr, cdata.lookup_item(id)) +} + pub fn maybe_get_item_ast<'a, 'tcx>(cdata: Cmd, tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefIndex) -> FoundAst<'tcx> { debug!("Looking up item: {:?}", id); diff --git a/src/librustc_trans/monomorphize.rs b/src/librustc_trans/monomorphize.rs index c02dd7995f1e9..cf84dd57d0254 100644 --- a/src/librustc_trans/monomorphize.rs +++ b/src/librustc_trans/monomorphize.rs @@ -173,7 +173,7 @@ pub struct Instance<'tcx> { impl<'tcx> fmt::Display for Instance<'tcx> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ppaux::parameterized(f, &self.substs, self.def, ppaux::Ns::Value, &[], - |tcx| tcx.lookup_item_type(self.def).generics) + |tcx| Some(tcx.lookup_item_type(self.def).generics)) } } From 148f8422f3bf1c1e50a3773c71e333aa6b166f22 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 29 May 2016 21:57:58 +0300 Subject: [PATCH 06/11] check for is_cleanup violations in MIR typeck There weren't any in practice, but as these cause MSVC-only problems, the check looks like a good idea. --- src/librustc_mir/transform/type_check.rs | 68 ++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 7a41211381cb9..efac8ea846111 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -533,6 +533,69 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { } } + fn check_iscleanup(&mut self, mir: &Mir<'tcx>, block: &BasicBlockData<'tcx>) + { + let is_cleanup = block.is_cleanup; + self.last_span = block.terminator().span; + match block.terminator().kind { + TerminatorKind::Goto { target } => + self.assert_iscleanup(mir, block, target, is_cleanup), + TerminatorKind::If { targets: (on_true, on_false), .. } => { + self.assert_iscleanup(mir, block, on_true, is_cleanup); + self.assert_iscleanup(mir, block, on_false, is_cleanup); + } + TerminatorKind::Switch { ref targets, .. } | + TerminatorKind::SwitchInt { ref targets, .. } => { + for target in targets { + self.assert_iscleanup(mir, block, *target, is_cleanup); + } + } + TerminatorKind::Resume => { + if !is_cleanup { + span_mirbug!(self, block, "resume on non-cleanup block!") + } + } + TerminatorKind::Return => { + if is_cleanup { + span_mirbug!(self, block, "return on cleanup block") + } + } + TerminatorKind::Drop { target, unwind, .. } | + TerminatorKind::DropAndReplace { target, unwind, .. } => { + self.assert_iscleanup(mir, block, target, is_cleanup); + if let Some(unwind) = unwind { + if is_cleanup { + span_mirbug!(self, block, "unwind on cleanup block") + } + self.assert_iscleanup(mir, block, unwind, true); + } + } + TerminatorKind::Call { ref destination, cleanup, .. } => { + if let &Some((_, target)) = destination { + self.assert_iscleanup(mir, block, target, is_cleanup); + } + if let Some(cleanup) = cleanup { + if is_cleanup { + span_mirbug!(self, block, "cleanup on cleanup block") + } + self.assert_iscleanup(mir, block, cleanup, true); + } + } + } + } + + fn assert_iscleanup(&mut self, + mir: &Mir<'tcx>, + ctxt: &fmt::Debug, + bb: BasicBlock, + iscleanuppad: bool) + { + if mir.basic_block_data(bb).is_cleanup != iscleanuppad { + span_mirbug!(self, ctxt, "cleanuppad mismatch: {:?} should be {:?}", + bb, iscleanuppad); + } + } + fn typeck_mir(&mut self, mir: &Mir<'tcx>) { self.last_span = mir.span; debug!("run_on_mir: {:?}", mir.span); @@ -544,9 +607,8 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> { self.check_stmt(mir, stmt); } - if let Some(ref terminator) = block.terminator { - self.check_terminator(mir, terminator); - } + self.check_terminator(mir, block.terminator()); + self.check_iscleanup(mir, block); } } From 506086ef9661e124280d46673b3ed93be6461ae8 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 4 Jun 2016 01:49:34 +0300 Subject: [PATCH 07/11] jump to the cleanup block in the unwind path for open_drop_for_box silly bug. Hopefully the last one. --- .../borrowck/mir/elaborate_drops.rs | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index 6e5303626f116..cbe923ead9f13 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -694,7 +694,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { targets: variant_drops }); - self.drop_flag_test_block(c, c.is_cleanup, switch_block) + self.drop_flag_test_block(c, switch_block) } } } @@ -749,7 +749,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { ); } - self.drop_flag_test_block(c, c.is_cleanup, drop_block) + self.drop_flag_test_block(c, drop_block) } /// Create a simple conditional drop. @@ -764,7 +764,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let drop_bb = self.drop_block(c); self.drop_flags_for_drop(c, drop_bb); - self.drop_flag_test_block(c, c.is_cleanup, drop_bb) + self.drop_flag_test_block(c, drop_bb) } fn new_block<'a>(&mut self, @@ -791,22 +791,30 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn drop_flag_test_block<'a>(&mut self, c: &DropCtxt<'a, 'tcx>, - is_cleanup: bool, on_set: BasicBlock) - -> BasicBlock + -> BasicBlock { + self.drop_flag_test_block_with_succ(c, c.is_cleanup, on_set, c.succ) + } + + fn drop_flag_test_block_with_succ<'a>(&mut self, + c: &DropCtxt<'a, 'tcx>, + is_cleanup: bool, + on_set: BasicBlock, + on_unset: BasicBlock) + -> BasicBlock { let (maybe_live, maybe_dead) = c.init_data.state(c.path); debug!("drop_flag_test_block({:?},{:?},{:?}) - {:?}", c, is_cleanup, on_set, (maybe_live, maybe_dead)); match (maybe_live, maybe_dead) { - (false, _) => c.succ, + (false, _) => on_unset, (true, false) => on_set, (true, true) => { let flag = self.drop_flag(c.path).unwrap(); self.new_block(c, is_cleanup, TerminatorKind::If { cond: Operand::Consume(flag), - targets: (on_set, c.succ) + targets: (on_set, on_unset) }) } } @@ -841,7 +849,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { is_cleanup: bool ) -> BasicBlock { let block = self.unelaborated_free_block(c, ty, target, is_cleanup); - self.drop_flag_test_block(c, is_cleanup, block) + self.drop_flag_test_block_with_succ(c, is_cleanup, block, target) } fn unelaborated_free_block<'a>( From 1ae7ae0c1c7ed68c616273f245647afa47f3cbde Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 29 May 2016 22:01:06 +0300 Subject: [PATCH 08/11] fix translation of terminators in MSVC cleanup blocks MSVC requires unwinding code to be split to a tree of *funclets*, where each funclet can only branch to itself or to to its parent. Luckily, the code we generates matches this pattern. Recover that structure in an analyze pass and translate according to that. --- .../borrowck/mir/elaborate_drops.rs | 6 +- src/librustc_trans/common.rs | 24 ++- src/librustc_trans/mir/analyze.rs | 103 +++++++++ src/librustc_trans/mir/block.rs | 200 +++++++++++------- src/librustc_trans/mir/mod.rs | 26 ++- src/test/run-fail/issue-30380.rs | 3 + src/test/run-pass/dynamic-drop.rs | 90 ++++++-- 7 files changed, 347 insertions(+), 105 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index cbe923ead9f13..f72e10d99cfd1 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -998,9 +998,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { continue } TerminatorKind::DropAndReplace { .. } => { - // this contains the consume of the source and + // this contains the move of the source and // the initialization of the destination. We - // only want the latter + // only want the former - the latter is handled + // by the elaboration code and must be done + // *after* the destination is dropped. assert!(self.patch.is_patched(bb)); allow_initializations = false; } diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs index c1685e6a74904..bf62849818d4e 100644 --- a/src/librustc_trans/common.rs +++ b/src/librustc_trans/common.rs @@ -577,6 +577,15 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> { self.lpad.get() } + pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) { + // FIXME: use an IVar? + self.lpad.set(lpad); + } + + pub fn set_lpad(&self, lpad: Option) { + self.set_lpad_ref(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p))) + } + pub fn mir(&self) -> CachedMir<'blk, 'tcx> { self.fcx.mir() } @@ -716,7 +725,16 @@ impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> { } pub fn set_lpad(&self, lpad: Option) { - self.bcx.lpad.set(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p))) + self.bcx.set_lpad(lpad) + } + + pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) { + // FIXME: use an IVar? + self.bcx.set_lpad_ref(lpad); + } + + pub fn lpad(&self) -> Option<&'blk LandingPad> { + self.bcx.lpad() } } @@ -761,6 +779,10 @@ impl LandingPad { pub fn bundle(&self) -> Option<&OperandBundleDef> { self.operand.as_ref() } + + pub fn cleanuppad(&self) -> Option { + self.cleanuppad + } } impl Clone for LandingPad { diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs index 0b88ba554da67..03df1c451f0d1 100644 --- a/src/librustc_trans/mir/analyze.rs +++ b/src/librustc_trans/mir/analyze.rs @@ -13,7 +13,9 @@ use rustc_data_structures::bitvec::BitVector; use rustc::mir::repr as mir; +use rustc::mir::repr::TerminatorKind; use rustc::mir::visit::{Visitor, LvalueContext}; +use rustc_mir::traversal; use common::{self, Block, BlockAndBuilder}; use super::rvalue; @@ -134,3 +136,104 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> { self.super_lvalue(lvalue, context); } } + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CleanupKind { + NotCleanup, + Funclet, + Internal { funclet: mir::BasicBlock } +} + +pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>, + mir: &mir::Mir<'tcx>) + -> Vec +{ + fn discover_masters<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) { + for bb in mir.all_basic_blocks() { + let data = mir.basic_block_data(bb); + match data.terminator().kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Return | + TerminatorKind::If { .. } | + TerminatorKind::Switch { .. } | + TerminatorKind::SwitchInt { .. } => { + /* nothing to do */ + } + TerminatorKind::Call { cleanup: unwind, .. } | + TerminatorKind::DropAndReplace { unwind, .. } | + TerminatorKind::Drop { unwind, .. } => { + if let Some(unwind) = unwind { + debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet", + bb, data, unwind); + result[unwind.index()] = CleanupKind::Funclet; + } + } + } + } + } + + fn propagate<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) { + let mut funclet_succs : Vec<_> = + mir.all_basic_blocks().iter().map(|_| None).collect(); + + let mut set_successor = |funclet: mir::BasicBlock, succ| { + match funclet_succs[funclet.index()] { + ref mut s @ None => { + debug!("set_successor: updating successor of {:?} to {:?}", + funclet, succ); + *s = Some(succ); + }, + Some(s) => if s != succ { + span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}", + funclet, s, succ); + } + } + }; + + for (bb, data) in traversal::reverse_postorder(mir) { + let funclet = match result[bb.index()] { + CleanupKind::NotCleanup => continue, + CleanupKind::Funclet => bb, + CleanupKind::Internal { funclet } => funclet, + }; + + debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}", + bb, data, result[bb.index()], funclet); + + for &succ in data.terminator().successors().iter() { + let kind = result[succ.index()]; + debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", + funclet, succ, kind); + match kind { + CleanupKind::NotCleanup => { + result[succ.index()] = CleanupKind::Internal { funclet: funclet }; + } + CleanupKind::Funclet => { + set_successor(funclet, succ); + } + CleanupKind::Internal { funclet: succ_funclet } => { + if funclet != succ_funclet { + // `succ` has 2 different funclet going into it, so it must + // be a funclet by itself. + + debug!("promoting {:?} to a funclet and updating {:?}", succ, + succ_funclet); + result[succ.index()] = CleanupKind::Funclet; + set_successor(succ_funclet, succ); + set_successor(funclet, succ); + } + } + } + } + } + } + + let mut result : Vec<_> = + mir.all_basic_blocks().iter().map(|_| CleanupKind::NotCleanup).collect(); + + discover_masters(&mut result, mir); + propagate(&mut result, mir); + debug!("cleanup_kinds: result={:?}", result); + result +} diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs index a917327b0523a..eb962b6615442 100644 --- a/src/librustc_trans/mir/block.rs +++ b/src/librustc_trans/mir/block.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::{self, BasicBlockRef, ValueRef, OperandBundleDef}; +use llvm::{self, ValueRef}; use rustc::ty; use rustc::mir::repr as mir; use abi::{Abi, FnType, ArgType}; @@ -16,7 +16,7 @@ use adt; use base; use build; use callee::{Callee, CalleeData, Fn, Intrinsic, NamedTupleConstructor, Virtual}; -use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, C_undef}; +use common::{self, type_is_fat_ptr, Block, BlockAndBuilder, LandingPad, C_undef}; use debuginfo::DebugLoc; use Disr; use machine::{llalign_of_min, llbitsize_of_real}; @@ -27,6 +27,7 @@ use type_::Type; use rustc_data_structures::fnv::FnvHashMap; use super::{MirContext, TempRef}; +use super::analyze::CleanupKind; use super::constant::Const; use super::lvalue::{LvalueRef, load_fat_ptr}; use super::operand::OperandRef; @@ -34,22 +35,62 @@ use super::operand::OperandValue::{self, FatPtr, Immediate, Ref}; impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { pub fn trans_block(&mut self, bb: mir::BasicBlock) { - debug!("trans_block({:?})", bb); - let mut bcx = self.bcx(bb); let mir = self.mir.clone(); let data = mir.basic_block_data(bb); - // MSVC SEH bits - let (cleanup_pad, cleanup_bundle) = if let Some((cp, cb)) = self.make_cleanup_pad(bb) { - (Some(cp), Some(cb)) - } else { - (None, None) + debug!("trans_block({:?}={:?})", bb, data); + + // Create the cleanup bundle, if needed. + let cleanup_pad = bcx.lpad().and_then(|lp| lp.cleanuppad()); + let cleanup_bundle = bcx.lpad().and_then(|l| l.bundle()); + + let funclet_br = |this: &Self, bcx: BlockAndBuilder, bb: mir::BasicBlock| { + let lltarget = this.blocks[bb.index()].llbb; + if let Some(cp) = cleanup_pad { + match this.cleanup_kind(bb) { + CleanupKind::Funclet => { + // micro-optimization: generate a `ret` rather than a jump + // to a return block + bcx.cleanup_ret(cp, Some(lltarget)); + } + CleanupKind::Internal { .. } => bcx.br(lltarget), + CleanupKind::NotCleanup => bug!("jump from cleanup bb to bb {:?}", bb) + } + } else { + bcx.br(lltarget); + } }; - let funclet_br = |bcx: BlockAndBuilder, llbb: BasicBlockRef| if let Some(cp) = cleanup_pad { - bcx.cleanup_ret(cp, Some(llbb)); - } else { - bcx.br(llbb); + + let llblock = |this: &mut Self, target: mir::BasicBlock| { + let lltarget = this.blocks[target.index()].llbb; + + if let Some(cp) = cleanup_pad { + match this.cleanup_kind(target) { + CleanupKind::Funclet => { + // MSVC cross-funclet jump - need a trampoline + + debug!("llblock: creating cleanup trampoline for {:?}", target); + let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); + let trampoline = this.fcx.new_block(name, None).build(); + trampoline.set_personality_fn(this.fcx.eh_personality()); + trampoline.cleanup_ret(cp, Some(lltarget)); + trampoline.llbb() + } + CleanupKind::Internal { .. } => lltarget, + CleanupKind::NotCleanup => + bug!("jump from cleanup bb {:?} to bb {:?}", bb, target) + } + } else { + if let (CleanupKind::NotCleanup, CleanupKind::Funclet) = + (this.cleanup_kind(bb), this.cleanup_kind(target)) + { + // jump *into* cleanup - need a landing pad if GNU + this.landing_pad_to(target).llbb + } else { + lltarget + } + } }; for statement in &data.statements { @@ -78,13 +119,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::TerminatorKind::Goto { target } => { - funclet_br(bcx, self.llblock(target)); + funclet_br(self, bcx, target); } mir::TerminatorKind::If { ref cond, targets: (true_bb, false_bb) } => { let cond = self.trans_operand(&bcx, cond); - let lltrue = self.llblock(true_bb); - let llfalse = self.llblock(false_bb); + + let lltrue = llblock(self, true_bb); + let llfalse = llblock(self, false_bb); bcx.cond_br(cond.immediate(), lltrue, llfalse); } @@ -106,18 +148,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // code. This is especially helpful in cases like an if-let on a huge enum. // Note: This optimization is only valid for exhaustive matches. Some((&&bb, &c)) if c > targets.len() / 2 => { - (Some(bb), self.blocks[bb.index()]) + (Some(bb), llblock(self, bb)) } // We're generating an exhaustive switch, so the else branch // can't be hit. Branching to an unreachable instruction // lets LLVM know this - _ => (None, self.unreachable_block()) + _ => (None, self.unreachable_block().llbb) }; - let switch = bcx.switch(discr, default_blk.llbb, targets.len()); + let switch = bcx.switch(discr, default_blk, targets.len()); assert_eq!(adt_def.variants.len(), targets.len()); for (adt_variant, &target) in adt_def.variants.iter().zip(targets) { if default_bb != Some(target) { - let llbb = self.llblock(target); + let llbb = llblock(self, target); let llval = bcx.with_block(|bcx| adt::trans_case( bcx, &repr, Disr::from(adt_variant.disr_val))); build::AddCase(switch, llval, llbb) @@ -129,10 +171,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let (otherwise, targets) = targets.split_last().unwrap(); let discr = bcx.load(self.trans_lvalue(&bcx, discr).llval); let discr = bcx.with_block(|bcx| base::to_immediate(bcx, discr, switch_ty)); - let switch = bcx.switch(discr, self.llblock(*otherwise), values.len()); + let switch = bcx.switch(discr, llblock(self, *otherwise), values.len()); for (value, target) in values.iter().zip(targets) { let val = Const::from_constval(bcx.ccx(), value.clone(), switch_ty); - let llbb = self.llblock(*target); + let llbb = llblock(self, *target); build::AddCase(switch, val.llval, llbb) } } @@ -148,7 +190,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let ty = lvalue.ty.to_ty(bcx.tcx()); // Double check for necessity to drop if !glue::type_needs_drop(bcx.tcx(), ty) { - funclet_br(bcx, self.llblock(target)); + funclet_br(self, bcx, target); return; } let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); @@ -159,19 +201,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { lvalue.llval }; if let Some(unwind) = unwind { - let uwbcx = self.bcx(unwind); - let unwind = self.make_landing_pad(uwbcx); bcx.invoke(drop_fn, &[llvalue], - self.llblock(target), - unwind.llbb(), - cleanup_bundle.as_ref()); - self.bcx(target).at_start(|bcx| { - debug_loc.apply_to_bcx(bcx); - }); + self.blocks[target.index()].llbb, + llblock(self, unwind), + cleanup_bundle); } else { - bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref()); - funclet_br(bcx, self.llblock(target)); + bcx.call(drop_fn, &[llvalue], cleanup_bundle); + funclet_br(self, bcx, target); } } @@ -213,7 +250,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llptr = self.trans_operand(&bcx, &args[0]).immediate(); let val = self.trans_operand(&bcx, &args[1]); self.store_operand(&bcx, llptr, val); - funclet_br(bcx, self.llblock(target)); + funclet_br(self, bcx, target); return; } @@ -223,7 +260,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { this.trans_transmute(&bcx, &args[0], dest); }); - funclet_br(bcx, self.llblock(target)); + funclet_br(self, bcx, target); return; } @@ -328,7 +365,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } if let Some((_, target)) = *destination { - funclet_br(bcx, self.llblock(target)); + funclet_br(self, bcx, target); } else { // trans_intrinsic_call already used Unreachable. // bcx.unreachable(); @@ -341,19 +378,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; // Many different ways to call a function handled here - if let Some(cleanup) = cleanup.map(|bb| self.bcx(bb)) { + if let &Some(cleanup) = cleanup { let ret_bcx = if let Some((_, target)) = *destination { self.blocks[target.index()] } else { self.unreachable_block() }; - let landingpad = self.make_landing_pad(cleanup); - let invokeret = bcx.invoke(fn_ptr, &llargs, ret_bcx.llbb, - landingpad.llbb(), - cleanup_bundle.as_ref()); + llblock(self, cleanup), + cleanup_bundle); fn_ty.apply_attrs_callsite(invokeret); if destination.is_some() { @@ -368,7 +403,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }); } } else { - let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle.as_ref()); + let llret = bcx.call(fn_ptr, &llargs, cleanup_bundle); fn_ty.apply_attrs_callsite(llret); if let Some((_, target)) = *destination { let op = OperandRef { @@ -376,9 +411,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ty: sig.output.unwrap() }; self.store_return(&bcx, ret_dest, fn_ty.ret, op); - funclet_br(bcx, self.llblock(target)); + funclet_br(self, bcx, target); } else { - // no need to drop args, because the call never returns bcx.unreachable(); } } @@ -518,17 +552,29 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - /// Create a landingpad wrapper around the given Block. + fn cleanup_kind(&self, bb: mir::BasicBlock) -> CleanupKind { + self.cleanup_kinds[bb.index()] + } + + /// Return the landingpad wrapper around the given basic block /// /// No-op in MSVC SEH scheme. - fn make_landing_pad(&mut self, - cleanup: BlockAndBuilder<'bcx, 'tcx>) - -> BlockAndBuilder<'bcx, 'tcx> + fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { - if base::wants_msvc_seh(cleanup.sess()) { - return cleanup; + if let Some(block) = self.landing_pads[target_bb.index()] { + return block; + } + + if base::wants_msvc_seh(self.fcx.ccx.sess()) { + return self.blocks[target_bb.index()]; } - let bcx = self.fcx.new_block("cleanup", None).build(); + + let target = self.bcx(target_bb); + + let block = self.fcx.new_block("cleanup", None); + self.landing_pads[target_bb.index()] = Some(block); + + let bcx = block.build(); let ccx = bcx.ccx(); let llpersonality = self.fcx.eh_personality(); let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); @@ -536,36 +582,34 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx.set_cleanup(llretval); let slot = self.get_personality_slot(&bcx); bcx.store(llretval, slot); - bcx.br(cleanup.llbb()); - bcx + bcx.br(target.llbb()); + block } - /// Create prologue cleanuppad instruction under MSVC SEH handling scheme. - /// - /// Also handles setting some state for the original trans and creating an operand bundle for - /// function calls. - fn make_cleanup_pad(&mut self, bb: mir::BasicBlock) -> Option<(ValueRef, OperandBundleDef)> { + pub fn init_cpad(&mut self, bb: mir::BasicBlock) { let bcx = self.bcx(bb); let data = self.mir.basic_block_data(bb); - let use_funclets = base::wants_msvc_seh(bcx.sess()) && data.is_cleanup; - let cleanup_pad = if use_funclets { - bcx.set_personality_fn(self.fcx.eh_personality()); - bcx.at_start(|bcx| { - DebugLoc::None.apply_to_bcx(bcx); - Some(bcx.cleanup_pad(None, &[])) - }) - } else { - None + debug!("init_cpad({:?})", data); + + match self.cleanup_kinds[bb.index()] { + CleanupKind::NotCleanup => { + bcx.set_lpad(None) + } + _ if !base::wants_msvc_seh(bcx.sess()) => { + bcx.set_lpad(Some(LandingPad::gnu())) + } + CleanupKind::Internal { funclet } => { + // FIXME: is this needed? + bcx.set_personality_fn(self.fcx.eh_personality()); + bcx.set_lpad_ref(self.bcx(funclet).lpad()); + } + CleanupKind::Funclet => { + bcx.set_personality_fn(self.fcx.eh_personality()); + DebugLoc::None.apply_to_bcx(&bcx); + let cleanup_pad = bcx.cleanup_pad(None, &[]); + bcx.set_lpad(Some(LandingPad::msvc(cleanup_pad))); + } }; - // Set the landingpad global-state for old translator, so it knows about the SEH used. - bcx.set_lpad(if let Some(cleanup_pad) = cleanup_pad { - Some(common::LandingPad::msvc(cleanup_pad)) - } else if data.is_cleanup { - Some(common::LandingPad::gnu()) - } else { - None - }); - cleanup_pad.map(|f| (f, OperandBundleDef::new("funclet", &[f]))) } fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> { @@ -581,10 +625,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { self.blocks[bb.index()].build() } - pub fn llblock(&self, bb: mir::BasicBlock) -> BasicBlockRef { - self.blocks[bb.index()].llbb - } - fn make_return_dest(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, dest: &mir::Lvalue<'tcx>, fn_ret_ty: &ArgType, llargs: &mut Vec, is_intrinsic: bool) -> ReturnDest { diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs index 3ff304758116c..d1206550b13d6 100644 --- a/src/librustc_trans/mir/mod.rs +++ b/src/librustc_trans/mir/mod.rs @@ -73,6 +73,13 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// A `Block` for each MIR `BasicBlock` blocks: Vec>, + /// The funclet status of each basic block + cleanup_kinds: Vec, + + /// This stores the landing-pad block for a given BB, computed lazily on GNU + /// and eagerly on MSVC. + landing_pads: Vec>>, + /// Cached unreachable block unreachable_block: Option>, @@ -139,8 +146,9 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { // Analyze the temps to determine which must be lvalues // FIXME - let lvalue_temps = bcx.with_block(|bcx| { - analyze::lvalue_temps(bcx, &mir) + let (lvalue_temps, cleanup_kinds) = bcx.with_block(|bcx| { + (analyze::lvalue_temps(bcx, &mir), + analyze::cleanup_kinds(bcx, &mir)) }); // Compute debuginfo scopes from MIR scopes. @@ -206,6 +214,8 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { llpersonalityslot: None, blocks: block_bcxs, unreachable_block: None, + cleanup_kinds: cleanup_kinds, + landing_pads: mir_blocks.iter().map(|_| None).collect(), vars: vars, temps: temps, args: args, @@ -214,7 +224,14 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let mut visited = BitVector::new(mir_blocks.len()); - let rpo = traversal::reverse_postorder(&mir); + let mut rpo = traversal::reverse_postorder(&mir); + + // Prepare each block for translation. + for (bb, _) in rpo.by_ref() { + mircx.init_cpad(bb); + } + rpo.reset(); + // Translate the body of each block using reverse postorder for (bb, _) in rpo { visited.insert(bb.index()); @@ -228,8 +245,7 @@ pub fn trans_mir<'blk, 'tcx: 'blk>(fcx: &'blk FunctionContext<'blk, 'tcx>) { let block = BasicBlock(block.llbb); // Unreachable block if !visited.contains(bb.index()) { - block.delete(); - } else if block.pred_iter().count() == 0 { + debug!("trans_mir: block {:?} was not visited", bb); block.delete(); } } diff --git a/src/test/run-fail/issue-30380.rs b/src/test/run-fail/issue-30380.rs index 7bd9adcba9bd1..eb668517bdf34 100644 --- a/src/test/run-fail/issue-30380.rs +++ b/src/test/run-fail/issue-30380.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(rustc_attrs)] + // check that panics in destructors during assignment do not leave // destroyed values lying around for other destructors to observe. @@ -33,6 +35,7 @@ impl<'a> Drop for Observer<'a> { } } +#[rustc_mir] fn foo(b: &mut Observer) { *b.0 = FilledOnDrop(1); } diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 48e7b7ca57696..f917531e868f1 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -8,12 +8,23 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::cell::RefCell; +#![feature(rustc_attrs)] + +use std::cell::{Cell, RefCell}; +use std::panic; +use std::usize; + +struct InjectedFailure; struct Allocator { data: RefCell>, + failing_op: usize, + cur_ops: Cell, } +impl panic::UnwindSafe for Allocator {} +impl panic::RefUnwindSafe for Allocator {} + impl Drop for Allocator { fn drop(&mut self) { let data = self.data.borrow(); @@ -24,8 +35,20 @@ impl Drop for Allocator { } impl Allocator { - fn new() -> Self { Allocator { data: RefCell::new(vec![]) } } + fn new(failing_op: usize) -> Self { + Allocator { + failing_op: failing_op, + cur_ops: Cell::new(0), + data: RefCell::new(vec![]) + } + } fn alloc(&self) -> Ptr { + self.cur_ops.set(self.cur_ops.get() + 1); + + if self.cur_ops.get() == self.failing_op { + panic!(InjectedFailure); + } + let mut data = self.data.borrow_mut(); let addr = data.len(); data.push(true); @@ -42,9 +65,16 @@ impl<'a> Drop for Ptr<'a> { } ref mut d => *d = false } + + self.1.cur_ops.set(self.1.cur_ops.get()+1); + + if self.1.cur_ops.get() == self.1.failing_op { + panic!(InjectedFailure); + } } } +#[rustc_mir] fn dynamic_init(a: &Allocator, c: bool) { let _x; if c { @@ -52,15 +82,17 @@ fn dynamic_init(a: &Allocator, c: bool) { } } -fn dynamic_drop(a: &Allocator, c: bool) -> Option { +#[rustc_mir] +fn dynamic_drop(a: &Allocator, c: bool) { let x = a.alloc(); if c { Some(x) } else { None - } + }; } +#[rustc_mir] fn assignment2(a: &Allocator, c0: bool, c1: bool) { let mut _v = a.alloc(); let mut _w = a.alloc(); @@ -73,6 +105,7 @@ fn assignment2(a: &Allocator, c0: bool, c1: bool) { } } +#[rustc_mir] fn assignment1(a: &Allocator, c0: bool) { let mut _v = a.alloc(); let mut _w = a.alloc(); @@ -82,19 +115,42 @@ fn assignment1(a: &Allocator, c0: bool) { _v = _w; } +fn run_test(mut f: F) + where F: FnMut(&Allocator) +{ + 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); + let alloc = &alloc; + let f = panic::AssertUnwindSafe(&mut f); + let result = panic::catch_unwind(move || { + f.0(alloc); + }); + match result { + Ok(..) => panic!("test executed {} ops but now {}", + first_alloc.cur_ops.get(), alloc.cur_ops.get()), + Err(e) => { + if e.downcast_ref::().is_none() { + panic::resume_unwind(e); + } + } + } + } +} fn main() { - let a = Allocator::new(); - dynamic_init(&a, false); - dynamic_init(&a, true); - dynamic_drop(&a, false); - dynamic_drop(&a, true); - - assignment2(&a, false, false); - assignment2(&a, false, true); - assignment2(&a, true, false); - assignment2(&a, true, true); - - assignment1(&a, false); - assignment1(&a, true); + 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)); } From 4248269f8ab7c749f2a626b33dc8fa9f270e8fd1 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 29 May 2016 23:53:20 +0300 Subject: [PATCH 09/11] fix fallout in tests --- .../codegen-units/item-collection/cross-crate-trait-method.rs | 2 -- src/test/codegen-units/item-collection/generic-functions.rs | 2 -- src/test/codegen-units/partitioning/local-generic.rs | 2 -- src/test/run-make/atomic-lock-free/atomic_lock_free.rs | 2 ++ 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs index 9f29a90bffbf6..195125793be8a 100644 --- a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs +++ b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs @@ -56,5 +56,3 @@ fn main() //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] let _: (char, bool) = Trait::without_default_impl_generic(false); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/generic-functions.rs b/src/test/codegen-units/item-collection/generic-functions.rs index 5ec1f7fbc3ca3..afe6ffc8bfe03 100644 --- a/src/test/codegen-units/item-collection/generic-functions.rs +++ b/src/test/codegen-units/item-collection/generic-functions.rs @@ -60,5 +60,3 @@ fn main() { //~ TRANS_ITEM fn generic_functions::foo3[0] let _ = foo3('v', (), ()); } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/local-generic.rs b/src/test/codegen-units/partitioning/local-generic.rs index e38e676b95c61..f5641f1f2ed73 100644 --- a/src/test/codegen-units/partitioning/local-generic.rs +++ b/src/test/codegen-units/partitioning/local-generic.rs @@ -56,5 +56,3 @@ mod mod2 { let _ = generic("abc"); } } - -//~ TRANS_ITEM drop-glue i8 diff --git a/src/test/run-make/atomic-lock-free/atomic_lock_free.rs b/src/test/run-make/atomic-lock-free/atomic_lock_free.rs index 8731cd960f33c..023f2218b87ae 100644 --- a/src/test/run-make/atomic-lock-free/atomic_lock_free.rs +++ b/src/test/run-make/atomic-lock-free/atomic_lock_free.rs @@ -18,6 +18,8 @@ extern "rust-intrinsic" { #[lang = "sized"] trait Sized {} +#[lang = "copy"] +trait Copy {} #[cfg(target_has_atomic = "8")] pub unsafe fn atomic_u8(x: *mut u8) { From 4106ab24d74424b88c8e093071d9ece41bb67315 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 5 Jun 2016 09:00:17 +0300 Subject: [PATCH 10/11] break critical edges only when needed the *only* place where critical edges need to be broken is on Call instructions, so only break them there. --- .../borrowck/mir/elaborate_drops.rs | 60 ++++++++-------- src/librustc_driver/driver.rs | 5 +- ...ak_cleanup_edges.rs => add_call_guards.rs} | 69 ++++++++----------- src/librustc_mir/transform/dump_mir.rs | 27 ++++++++ src/librustc_mir/transform/mod.rs | 3 +- 5 files changed, 87 insertions(+), 77 deletions(-) rename src/librustc_mir/transform/{break_cleanup_edges.rs => add_call_guards.rs} (56%) create mode 100644 src/librustc_mir/transform/dump_mir.rs diff --git a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs index f72e10d99cfd1..e783420fa065c 100644 --- a/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs +++ b/src/librustc_borrowck/borrowck/mir/elaborate_drops.rs @@ -327,12 +327,30 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let data = self.mir.basic_block_data(bb); let terminator = data.terminator(); - let unwind = Some(unwind.unwrap_or_else(|| { - // we can't use the resume block directly, because we - // may want to add a drop flag write. - self.jump_to_resume_block(terminator.scope, - terminator.span) - })); + let assign = Statement { + kind: StatementKind::Assign(location.clone(), Rvalue::Use(value.clone())), + span: terminator.span, + scope: terminator.scope + }; + + let unwind = unwind.unwrap_or(self.patch.resume_block()); + let unwind = self.patch.new_block(BasicBlockData { + statements: vec![assign.clone()], + terminator: Some(Terminator { + kind: TerminatorKind::Goto { target: unwind }, + ..*terminator + }), + is_cleanup: true + }); + + let target = self.patch.new_block(BasicBlockData { + statements: vec![assign], + terminator: Some(Terminator { + kind: TerminatorKind::Goto { target: target }, + ..*terminator + }), + is_cleanup: data.is_cleanup, + }); if !self.lvalue_is_tracked(location) { // drop and replace behind a pointer/array/whatever. The location @@ -341,7 +359,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { self.patch.patch_terminator(bb, TerminatorKind::Drop { location: location.clone(), target: target, - unwind: unwind + unwind: Some(unwind) }); } else { debug!("elaborate_drop_and_replace({:?}) - tracked", terminator); @@ -356,24 +374,15 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { lvalue: location, path: path, succ: target, - unwind: unwind + unwind: Some(unwind) }, bb); on_all_children_bits(self.tcx, self.mir, self.move_data(), path, |child| { self.set_drop_flag(Location { block: target, index: 0 }, child, DropFlagState::Present); - if let Some(unwind) = unwind { - self.set_drop_flag(Location { block: unwind, index: 0 }, - child, DropFlagState::Present); - } + self.set_drop_flag(Location { block: unwind, index: 0 }, + child, DropFlagState::Present); }); } - - self.patch.add_assign(Location { block: target, index: 0 }, - location.clone(), Rvalue::Use(value.clone())); - if let Some(unwind) = unwind { - self.patch.add_assign(Location { block: unwind, index: 0 }, - location.clone(), Rvalue::Use(value.clone())); - } } /// This elaborates a single drop instruction, located at `bb`, and @@ -828,19 +837,6 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }) } - fn jump_to_resume_block<'a>(&mut self, scope: ScopeId, span: Span) -> BasicBlock { - let resume_block = self.patch.resume_block(); - self.patch.new_block(BasicBlockData { - statements: vec![], - terminator: Some(Terminator { - scope: scope, span: span, kind: TerminatorKind::Goto { - target: resume_block - } - }), - is_cleanup: true - }) - } - fn box_free_block<'a>( &mut self, c: &DropCtxt<'a, 'tcx>, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index bfad281702fd0..7c859d5e508bc 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -1032,11 +1032,12 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); passes.push_pass(box mir::transform::remove_dead_blocks::RemoveDeadBlocks); passes.push_pass(box mir::transform::erase_regions::EraseRegions); - passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges); + passes.push_pass(box mir::transform::add_call_guards::AddCallGuards); passes.push_pass(box borrowck::ElaborateDrops); passes.push_pass(box mir::transform::no_landing_pads::NoLandingPads); passes.push_pass(box mir::transform::simplify_cfg::SimplifyCfg); - passes.push_pass(box mir::transform::break_cleanup_edges::BreakCleanupEdges); + passes.push_pass(box mir::transform::add_call_guards::AddCallGuards); + passes.push_pass(box mir::transform::dump_mir::DumpMir("pre_trans")); passes.run_passes(tcx, &mut mir_map); }); diff --git a/src/librustc_mir/transform/break_cleanup_edges.rs b/src/librustc_mir/transform/add_call_guards.rs similarity index 56% rename from src/librustc_mir/transform/break_cleanup_edges.rs rename to src/librustc_mir/transform/add_call_guards.rs index 4902d31cf4d7a..bcdd62c189972 100644 --- a/src/librustc_mir/transform/break_cleanup_edges.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -12,13 +12,11 @@ use rustc::ty::TyCtxt; use rustc::mir::repr::*; use rustc::mir::transform::{MirPass, MirSource, Pass}; -use rustc_data_structures::bitvec::BitVector; - use pretty; use traversal; -pub struct BreakCleanupEdges; +pub struct AddCallGuards; /** * Breaks outgoing critical edges for call terminators in the MIR. @@ -40,7 +38,7 @@ pub struct BreakCleanupEdges; * */ -impl<'tcx> MirPass<'tcx> for BreakCleanupEdges { +impl<'tcx> MirPass<'tcx> for AddCallGuards { fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) { let mut pred_count = vec![0u32; mir.basic_blocks.len()]; @@ -53,9 +51,6 @@ impl<'tcx> MirPass<'tcx> for BreakCleanupEdges { } } - let cleanup_map : BitVector = mir.basic_blocks - .iter().map(|bb| bb.is_cleanup).collect(); - // We need a place to store the new blocks generated let mut new_blocks = Vec::new(); @@ -65,30 +60,31 @@ impl<'tcx> MirPass<'tcx> for BreakCleanupEdges { for &bb in &bbs { let data = mir.basic_block_data_mut(bb); - if let Some(ref mut term) = data.terminator { - if term_is_invoke(term) { - let term_span = term.span; - let term_scope = term.scope; - let succs = term.successors_mut(); - for tgt in succs { - let num_preds = pred_count[tgt.index()]; - if num_preds > 1 { - // It's a critical edge, break it - let goto = Terminator { - span: term_span, - scope: term_scope, - kind: TerminatorKind::Goto { target: *tgt } - }; - let mut data = BasicBlockData::new(Some(goto)); - data.is_cleanup = cleanup_map.contains(tgt.index()); - - // Get the index it will be when inserted into the MIR - let idx = cur_len + new_blocks.len(); - new_blocks.push(data); - *tgt = BasicBlock::new(idx); - } - } + match data.terminator { + Some(Terminator { + kind: TerminatorKind::Call { + destination: Some((_, ref mut destination)), + cleanup: Some(_), + .. + }, span, scope + }) if pred_count[destination.index()] > 1 => { + // It's a critical edge, break it + let call_guard = BasicBlockData { + statements: vec![], + is_cleanup: data.is_cleanup, + terminator: Some(Terminator { + span: span, + scope: scope, + kind: TerminatorKind::Goto { target: *destination } + }) + }; + + // Get the index it will be when inserted into the MIR + let idx = cur_len + new_blocks.len(); + new_blocks.push(call_guard); + *destination = BasicBlock::new(idx); } + _ => {} } } @@ -99,15 +95,4 @@ impl<'tcx> MirPass<'tcx> for BreakCleanupEdges { } } -impl Pass for BreakCleanupEdges {} - -// Returns true if the terminator is a call that would use an invoke in LLVM. -fn term_is_invoke(term: &Terminator) -> bool { - match term.kind { - TerminatorKind::Call { cleanup: Some(_), .. } | - // FIXME: not sure whether we need this one - TerminatorKind::Drop { unwind: Some(_), .. } | - TerminatorKind::DropAndReplace { .. } => true, - _ => false - } -} +impl Pass for AddCallGuards {} diff --git a/src/librustc_mir/transform/dump_mir.rs b/src/librustc_mir/transform/dump_mir.rs new file mode 100644 index 0000000000000..fb49f951ecd58 --- /dev/null +++ b/src/librustc_mir/transform/dump_mir.rs @@ -0,0 +1,27 @@ +// Copyright 2015 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. + +//! This pass just dumps MIR at a specified point. + +use rustc::ty::TyCtxt; +use rustc::mir::repr::*; +use rustc::mir::transform::{Pass, MirPass, MirSource}; +use pretty; + +pub struct DumpMir<'a>(pub &'a str); + +impl<'b, 'tcx> MirPass<'tcx> for DumpMir<'b> { + fn run_pass<'a>(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, mir: &mut Mir<'tcx>) { + pretty::dump_mir(tcx, self.0, &0, src, mir, None); + } +} + +impl<'b> Pass for DumpMir<'b> {} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 0dcb7ef84d01d..339dcdec06080 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -13,6 +13,7 @@ pub mod simplify_cfg; pub mod erase_regions; pub mod no_landing_pads; pub mod type_check; -pub mod break_cleanup_edges; +pub mod add_call_guards; pub mod promote_consts; pub mod qualify_consts; +pub mod dump_mir; From 063f8826e7addbe644e3dfd532736ce72d8990bc Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 5 Jun 2016 09:45:47 +0300 Subject: [PATCH 11/11] Update LLVM Picks up the fix for PR28005 --- src/llvm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/llvm b/src/llvm index a73c41e7f1c85..80ad955b60b3a 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit a73c41e7f1c85cd814e9792fc6a6a8f8e31b8dd4 +Subproject commit 80ad955b60b3ac02d0462a4a65fcea597d0ebfb1