From 2decde66c96ce18a3b529b54ea31f73232cf9766 Mon Sep 17 00:00:00 2001 From: Mikhail Modin Date: Tue, 12 Dec 2017 18:07:12 +0300 Subject: [PATCH 1/2] add subslice support in drop ladder for array --- src/librustc_mir/shim.rs | 13 +- src/librustc_mir/transform/elaborate_drops.rs | 140 ++++++++++++++---- src/librustc_mir/util/elaborate_drops.rs | 118 +++++++-------- src/test/run-pass/dynamic-drop.rs | 27 ++++ 4 files changed, 211 insertions(+), 87 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index 46193dedf8968..c7211e8f25dda 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -264,11 +264,15 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } - fn get_drop_flag(&mut self, _path: Self::Path) -> Option> { + fn get_drop_flags(&mut self, _path: Self::Path) -> Option> { None } - fn clear_drop_flag(&mut self, _location: Location, _path: Self::Path, _mode: DropFlagMode) { + fn clear_drop_flag(&mut self, + _location: Location, + _path: Self::Path, + _mode: DropFlagMode, + _opt_flag: Option) { } fn field_subpath(&self, _path: Self::Path, _field: Field) -> Option { @@ -280,8 +284,9 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { fn downcast_subpath(&self, _path: Self::Path, _variant: usize) -> Option { Some(()) } - fn array_subpath(&self, _path: Self::Path, _index: u32, _size: u32) -> Option { - None + fn array_subpaths(&self, _path: Self::Path, _size: u64) + -> Vec<(Place<'tcx>, Option, Option)> { + vec![] } } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index b075d2637da9b..f08a852510533 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -29,7 +29,7 @@ use util::elaborate_drops::{DropElaborator, DropStyle, DropFlagMode}; use syntax::ast; use syntax_pos::Span; -use std::fmt; +use std::{fmt, u32}; pub struct ElaborateDrops; @@ -74,6 +74,7 @@ impl MirPass for ElaborateDrops { flow_inits, flow_uninits, drop_flags: FxHashMap(), + array_items_drop_flags: FxHashMap(), patch: MirPatch::new(mir), }.elaborate() }; @@ -224,6 +225,7 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { ((some_live, some_dead), children_count != 1) } }; + match (maybe_live, maybe_dead, multipart) { (false, _, _) => DropStyle::Dead, (true, false, _) => DropStyle::Static, @@ -232,7 +234,16 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { } } - fn clear_drop_flag(&mut self, loc: Location, path: Self::Path, mode: DropFlagMode) { + fn clear_drop_flag( + &mut self, + loc: Location, + path: Self::Path, + mode: DropFlagMode, + opt_flag: Option) { + if let Some(flag) = opt_flag { + self.ctxt.set_drop_flag_impl(loc, flag, DropFlagState::Absent); + return; + } match mode { DropFlagMode::Shallow => { self.ctxt.set_drop_flag(loc, path, DropFlagState::Absent); @@ -257,18 +268,37 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { }) } - fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option { - dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| { - match p { - &Projection { - elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: false}, .. - } => offset == index, - &Projection { - elem: ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true}, .. - } => size - offset == index, - _ => false - } - }) + fn array_subpaths(&self, path: Self::Path, size: u64) + -> Vec<(Place<'tcx>, Option, Option)> { + if dataflow::move_path_children_matching(self.ctxt.move_data(), path, |_| { + assert!(size <= (u32::MAX as u64), + "move out check doesn't implemented for array bigger then u32"); + true + }).is_none() { + return vec![]; + } + + let size = size as u32; + let flags = self.ctxt.array_items_drop_flags.get(&path); + (0..size).map(|i| { + let place = &self.ctxt.move_data().move_paths[path].place; + (place.clone().elem(ProjectionElem::ConstantIndex{ + offset: i, + min_length: size, + from_end: false + }), + dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| + match p.elem { + ProjectionElem::ConstantIndex{offset, min_length: _, from_end: false} => + offset == i, + ProjectionElem::ConstantIndex{offset, min_length: _, from_end: true} => + size - offset == i, + ProjectionElem::Subslice{from, to} => from <= i && i < size - to, + _ => false + } + ), + flags.map(|f| f[i as usize])) + }).collect() } fn deref_subpath(&self, path: Self::Path) -> Option { @@ -291,18 +321,28 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { }) } - fn get_drop_flag(&mut self, path: Self::Path) -> Option> { - self.ctxt.drop_flag(path).map(Operand::Copy) + fn get_drop_flags(&mut self, path: Self::Path) -> Option> { + self.ctxt.drop_flag(path).map(|f| match f{ + DropFlag::Single(l) => Operand::Copy(Place::Local(*l)), + DropFlag::Subslice(_) => + panic!("get_drop_flags shouldn't be calles for sublice move path") + }) } } +enum DropFlag { + Single(Local), + Subslice(Vec), +} + struct ElaborateDropsCtxt<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, env: &'a MoveDataParamEnv<'tcx, 'tcx>, flow_inits: DataflowResults>, flow_uninits: DataflowResults>, - drop_flags: FxHashMap, + drop_flags: FxHashMap, + array_items_drop_flags: FxHashMap>, patch: MirPatch<'tcx>, } @@ -329,15 +369,48 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) { let tcx = self.tcx; + if let Place::Projection( + box Projection{ref base, elem: ProjectionElem::Subslice{from, to}}) = + self.move_data().move_paths[index].place { + let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); + if let ty::TyArray(_, n) = base_ty.sty { + let flags = { + let n = n.val.to_const_int().and_then(|v| v.to_u64()) + .expect("expected u64 size") as usize; + let span = self.mir.span; + let parent_index = self.move_data().move_paths[index].parent + .expect("subslice has parent"); + let patch = &mut self.patch; + let array_flags = self.array_items_drop_flags.entry(parent_index) + .or_insert_with(|| { + let flags = (0..n).map(|_| patch.new_internal(tcx.types.bool, span)) + .collect(); + debug!("create_drop_flags for array with subslice({:?}, {:?}, {:?})", + parent_index, span, flags); + flags + }); + let from = from as usize; + let to = to as usize; + array_flags[from .. n-to].iter().map(|x| *x).collect() + }; + + let span = self.mir.span; + self.drop_flags.entry(index).or_insert_with(|| { + debug!("create_drop_flags for subslice({:?}, {:?}, {:?})", index, span, flags); + DropFlag::Subslice(flags) + }); + return; + } + } let patch = &mut self.patch; debug!("create_drop_flag({:?})", self.mir.span); self.drop_flags.entry(index).or_insert_with(|| { - patch.new_internal(tcx.types.bool, span) + DropFlag::Single(patch.new_internal(tcx.types.bool, span)) }); } - fn drop_flag(&mut self, index: MovePathIndex) -> Option> { - self.drop_flags.get(&index).map(|t| Place::Local(*t)) + fn drop_flag(&mut self, index: MovePathIndex) -> Option<&DropFlag> { + self.drop_flags.get(&index) } /// create a patch that elaborates all drops in the input @@ -389,6 +462,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } }; + on_all_drop_children_bits(self.tcx, self.mir, self.env, path, |child| { let (maybe_live, maybe_dead) = init_data.state(child); debug!("collect_drop_flags: collecting {:?} from {:?}@{:?} - {:?}", @@ -548,11 +622,20 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }))) } + fn set_drop_flag_impl(&mut self, loc: Location, flag: Local, val: DropFlagState) { + let span = self.patch.source_info_for_location(self.mir, loc).span; + let val = self.constant_bool(span, val.value()); + self.patch.add_assign(loc, Place::Local(flag), 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.source_info_for_location(self.mir, loc).span; - let val = self.constant_bool(span, val.value()); - self.patch.add_assign(loc, Place::Local(flag), val); + match self.drop_flags.get(&path) { + Some(DropFlag::Single(flag)) => self.set_drop_flag_impl(loc, *flag, val), + Some(DropFlag::Subslice(flags)) => + for flag in flags { + self.set_drop_flag_impl(loc, *flag, val); + } + _ => {} } } @@ -561,7 +644,14 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { let span = self.patch.source_info_for_location(self.mir, loc).span; let false_ = self.constant_bool(span, false); for flag in self.drop_flags.values() { - self.patch.add_assign(loc, Place::Local(*flag), false_.clone()); + match flag { + DropFlag::Single(flag) => + self.patch.add_assign(loc, Place::Local(*flag), false_.clone()), + DropFlag::Subslice(flags) => + for flag in flags { + self.patch.add_assign(loc, Place::Local(*flag), false_.clone()); + }, + } } } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 3331bc9e59e0b..8015558b1a602 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -19,7 +19,7 @@ use rustc::ty::util::IntTypeExt; use rustc_data_structures::indexed_vec::Idx; use util::patch::MirPatch; -use std::{iter, u32}; +use std::iter; #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum DropFlagState { @@ -88,14 +88,19 @@ pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug { fn param_env(&self) -> ty::ParamEnv<'tcx>; fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; - fn get_drop_flag(&mut self, path: Self::Path) -> Option>; - fn clear_drop_flag(&mut self, location: Location, path: Self::Path, mode: DropFlagMode); + fn get_drop_flags(&mut self, path: Self::Path) -> Option>; + fn clear_drop_flag(&mut self, + location: Location, path: + Self::Path, + mode: DropFlagMode, + opt_drop_flag: Option); fn field_subpath(&self, path: Self::Path, field: Field) -> Option; fn deref_subpath(&self, path: Self::Path) -> Option; fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option; - fn array_subpath(&self, path: Self::Path, index: u32, size: u32) -> Option; + fn array_subpaths(&self, path: Self::Path, size: u64) + -> Vec<(Place<'tcx>, Option, Option)>; } #[derive(Debug)] @@ -124,7 +129,7 @@ pub fn elaborate_drop<'b, 'tcx, D>( { DropCtxt { elaborator, source_info, place, path, succ, unwind - }.elaborate_drop(bb) + }.elaborate_drop(bb, None) } impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> @@ -156,7 +161,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// /// FIXME: I think we should just control the flags externally /// and then we do not need this machinery. - pub fn elaborate_drop<'a>(&mut self, bb: BasicBlock) { + pub fn elaborate_drop<'a>(&mut self, bb: BasicBlock, opt_flag: Option) { debug!("elaborate_drop({:?})", self); let style = self.elaborator.drop_style(self.path, DropFlagMode::Deep); debug!("elaborate_drop({:?}): live - {:?}", self, style); @@ -168,7 +173,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> } DropStyle::Static => { let loc = self.terminator_loc(bb); - self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep); + self.elaborator.clear_drop_flag(loc, self.path, DropFlagMode::Deep, None); self.elaborator.patch().patch_terminator(bb, TerminatorKind::Drop { location: self.place.clone(), target: self.succ, @@ -178,7 +183,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> DropStyle::Conditional => { let unwind = self.unwind; // FIXME(#6393) let succ = self.succ; - let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind); + let drop_bb = self.complete_drop(Some(DropFlagMode::Deep), succ, unwind, opt_flag); self.elaborator.patch().patch_terminator(bb, TerminatorKind::Goto { target: drop_bb }); @@ -199,7 +204,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> variant_path: D::Path, variant: &'tcx ty::VariantDef, substs: &'tcx Substs<'tcx>) - -> Vec<(Place<'tcx>, Option)> + -> Vec<(Place<'tcx>, Option, Option)> { variant.fields.iter().enumerate().map(|(i, f)| { let field = Field::new(i); @@ -210,7 +215,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> &f.ty(self.tcx(), substs), self.elaborator.param_env() ); - (base_place.clone().field(field, field_ty), subpath) + (base_place.clone().field(field, field_ty), subpath, None) }).collect() } @@ -218,7 +223,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> place: &Place<'tcx>, path: Option, succ: BasicBlock, - unwind: Unwind) + unwind: Unwind, + opt_flag: Option) -> BasicBlock { if let Some(path) = path { @@ -228,7 +234,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> elaborator: self.elaborator, source_info: self.source_info, path, place, succ, unwind, - }.elaborated_drop_block() + }.elaborated_drop_block(opt_flag) } else { debug!("drop_subpath: for rest field {:?}", place); @@ -239,7 +245,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // Using `self.path` here to condition the drop on // our own drop flag. path: self.path - }.complete_drop(None, succ, unwind) + }.complete_drop(None, succ, unwind, opt_flag) } } @@ -252,13 +258,13 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn drop_halfladder(&mut self, unwind_ladder: &[Unwind], mut succ: BasicBlock, - fields: &[(Place<'tcx>, Option)]) + fields: &[(Place<'tcx>, Option, Option)]) -> Vec { Some(succ).into_iter().chain( fields.iter().rev().zip(unwind_ladder) - .map(|(&(ref place, path), &unwind_succ)| { - succ = self.drop_subpath(place, path, succ, unwind_succ); + .map(|(&(ref place, path, opt_flag), &unwind_succ)| { + succ = self.drop_subpath(place, path, succ, unwind_succ, opt_flag); succ }) ).collect() @@ -270,9 +276,9 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> // which is invalidated after the ADT is dropped. let (succ, unwind) = (self.succ, self.unwind); // FIXME(#6393) ( - self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind), + self.drop_flag_reset_block(DropFlagMode::Shallow, succ, unwind, None), unwind.map(|unwind| { - self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup) + self.drop_flag_reset_block(DropFlagMode::Shallow, unwind, Unwind::InCleanup, None) }) ) } @@ -295,7 +301,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> /// NOTE: this does not clear the master drop flag, so you need /// to point succ/unwind on a `drop_ladder_bottom`. fn drop_ladder<'a>(&mut self, - fields: Vec<(Place<'tcx>, Option)>, + fields: Vec<(Place<'tcx>, Option, Option)>, succ: BasicBlock, unwind: Unwind) -> (BasicBlock, Unwind) @@ -303,7 +309,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> debug!("drop_ladder({:?}, {:?})", self, fields); let mut fields = fields; - fields.retain(|&(ref place, _)| { + fields.retain(|&(ref place, ..)| { self.place_ty(place).needs_drop(self.tcx(), self.elaborator.param_env()) }); @@ -330,7 +336,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let fields = tys.iter().enumerate().map(|(i, &ty)| { (self.place.clone().field(Field::new(i), ty), - self.elaborator.field_subpath(self.path, Field::new(i))) + self.elaborator.field_subpath(self.path, Field::new(i)), + None) }).collect(); let (succ, unwind) = self.drop_ladder_bottom(); @@ -351,7 +358,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> self.box_free_block(ty, unwind, Unwind::InCleanup) }); - self.drop_subpath(&interior, interior_path, succ, unwind_succ) + self.drop_subpath(&interior, interior_path, succ, unwind_succ, None) } fn open_drop_for_adt<'a>(&mut self, adt: &'tcx ty::AdtDef, substs: &'tcx Substs<'tcx>) @@ -508,7 +515,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> is_cleanup: unwind.is_cleanup(), }; let switch_block = self.elaborator.patch().new_block(switch_block); - self.drop_flag_test_block(switch_block, succ, unwind) + self.drop_flag_test_block(switch_block, succ, unwind, None) } fn destructor_call_block<'a>(&mut self, (succ, unwind): (BasicBlock, Unwind)) @@ -636,31 +643,20 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn open_drop_for_array(&mut self, ety: Ty<'tcx>, opt_size: Option) -> BasicBlock { debug!("open_drop_for_array({:?}, {:?})", ety, opt_size); - // if size_of::() == 0 { - // index_based_loop - // } else { - // ptr_based_loop - // } - + // if any value has been moved out, creates the ladder like for tuples if let Some(size) = opt_size { - assert!(size <= (u32::MAX as u64), - "move out check doesn't implemented for array bigger then u32"); - let size = size as u32; - let fields: Vec<(Place<'tcx>, Option)> = (0..size).map(|i| { - (self.place.clone().elem(ProjectionElem::ConstantIndex{ - offset: i, - min_length: size, - from_end: false - }), - self.elaborator.array_subpath(self.path, i, size)) - }).collect(); - - if fields.iter().any(|(_,path)| path.is_some()) { + let fields = self.elaborator.array_subpaths(self.path, size); + if !fields.is_empty() { let (succ, unwind) = self.drop_ladder_bottom(); return self.drop_ladder(fields, succ, unwind).0 } } + // if size_of::() == 0 { + // index_based_loop + // } else { + // ptr_based_loop + // } let move_ = |place: &Place<'tcx>| Operand::Move(place.clone()); let tcx = self.tcx(); let size = &Place::Local(self.new_temp(tcx.types.usize)); @@ -758,8 +754,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> }); // FIXME(#34708): handle partially-dropped array/slice elements. - let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind); - self.drop_flag_test_block(reset_block, succ, unwind) + let reset_block = self.drop_flag_reset_block(DropFlagMode::Deep, drop_block, unwind, None); + self.drop_flag_test_block(reset_block, succ, unwind, None) } /// The slow-path - create an "open", elaborated drop for a type @@ -796,7 +792,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> ty::TyDynamic(..) => { let unwind = self.unwind; // FIXME(#6393) let succ = self.succ; - self.complete_drop(Some(DropFlagMode::Deep), succ, unwind) + self.complete_drop(Some(DropFlagMode::Deep), succ, unwind, None) } ty::TyArray(ety, size) => self.open_drop_for_array( ety, size.val.to_const_int().and_then(|v| v.to_u64())), @@ -816,39 +812,41 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn complete_drop<'a>(&mut self, drop_mode: Option, succ: BasicBlock, - unwind: Unwind) -> BasicBlock + unwind: Unwind, + opt_flag: Option) -> BasicBlock { debug!("complete_drop({:?},{:?})", self, drop_mode); let drop_block = self.drop_block(succ, unwind); let drop_block = if let Some(mode) = drop_mode { - self.drop_flag_reset_block(mode, drop_block, unwind) + self.drop_flag_reset_block(mode, drop_block, unwind, opt_flag) } else { drop_block }; - self.drop_flag_test_block(drop_block, succ, unwind) + self.drop_flag_test_block(drop_block, succ, unwind, opt_flag) } fn drop_flag_reset_block(&mut self, mode: DropFlagMode, succ: BasicBlock, - unwind: Unwind) -> BasicBlock + unwind: Unwind, + opt_flag: Option) -> BasicBlock { debug!("drop_flag_reset_block({:?},{:?})", self, mode); let block = self.new_block(unwind, TerminatorKind::Goto { target: succ }); let block_start = Location { block: block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, mode); + self.elaborator.clear_drop_flag(block_start, self.path, mode, opt_flag); block } - fn elaborated_drop_block<'a>(&mut self) -> BasicBlock { + fn elaborated_drop_block<'a>(&mut self, opt_flag: Option) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); let unwind = self.unwind; // FIXME(#6393) let succ = self.succ; let blk = self.drop_block(succ, unwind); - self.elaborate_drop(blk); + self.elaborate_drop(blk, opt_flag); blk } @@ -859,7 +857,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> unwind: Unwind, ) -> BasicBlock { let block = self.unelaborated_free_block(ty, target, unwind); - self.drop_flag_test_block(block, target, unwind) + self.drop_flag_test_block(block, target, unwind, None) } fn unelaborated_free_block<'a>( @@ -882,7 +880,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let free_block = self.new_block(unwind, call); let block_start = Location { block: free_block, statement_index: 0 }; - self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow, None); free_block } @@ -898,18 +896,22 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> fn drop_flag_test_block(&mut self, on_set: BasicBlock, on_unset: BasicBlock, - unwind: Unwind) + unwind: Unwind, + opt_flag: Option) -> BasicBlock { let style = self.elaborator.drop_style(self.path, DropFlagMode::Shallow); - debug!("drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?}", - self, on_set, on_unset, unwind, style); + debug!("drop_flag_test_block({:?},{:?},{:?},{:?}) - {:?} flag {:?}", + self, on_set, on_unset, unwind, style, opt_flag); match style { DropStyle::Dead => on_unset, DropStyle::Static => on_set, DropStyle::Conditional | DropStyle::Open => { - let flag = self.elaborator.get_drop_flag(self.path).unwrap(); + let flag = opt_flag + .map(|l| Operand::Copy(Place::Local(l))) + .or_else(|| self.elaborator.get_drop_flags(self.path)) + .unwrap(); let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset); self.new_block(unwind, term) } diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 09318e7256fd7..b165c6099098b 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -222,6 +222,27 @@ fn slice_pattern_one_of(a: &Allocator, i: usize) { }; } +fn subslice_pattern_begin(a: &Allocator) { + let[_x.., _] = [a.alloc(), a.alloc(), a.alloc()]; +} + +fn subslice_pattern_end(a: &Allocator) { + let[_x.., _] = [a.alloc(), a.alloc(), a.alloc()]; +} + +fn subslice_pattern_middle(a: &Allocator) { + let[_, _x.., _] = [a.alloc(), a.alloc(), a.alloc(), a.alloc(), a.alloc()]; +} + +fn subslice_pattern_by_arg(a: &Allocator, arg: bool) { + let a = [a.alloc(), a.alloc(), a.alloc(), a.alloc(), a.alloc()]; + if arg { + let[_x.., _] = a; + } else { + let[_, _y..] = a; + } +} + fn run_test(mut f: F) where F: FnMut(&Allocator) { @@ -300,5 +321,11 @@ fn main() { run_test(|a| slice_pattern_one_of(a, 2)); run_test(|a| slice_pattern_one_of(a, 3)); + run_test(|a| subslice_pattern_begin(a)); + run_test(|a| subslice_pattern_end(a)); + run_test(|a| subslice_pattern_middle(a)); + run_test(|a| subslice_pattern_by_arg(a, true)); + run_test(|a| subslice_pattern_by_arg(a, false)); + run_test_nopanic(|a| union1(a)); } From a4c7d75a114523447d65555f0cbe45716a29c3c1 Mon Sep 17 00:00:00 2001 From: Mikhail Modin Date: Thu, 14 Dec 2017 14:47:31 +0300 Subject: [PATCH 2/2] bugfix and add tests for elaborate array drop ladder --- src/librustc_mir/shim.rs | 2 +- src/librustc_mir/transform/elaborate_drops.rs | 116 +++++++++++------- src/librustc_mir/util/elaborate_drops.rs | 9 +- src/test/run-pass/dynamic-drop.rs | 32 +++++ 4 files changed, 110 insertions(+), 49 deletions(-) diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index c7211e8f25dda..59eeb81a07638 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -264,7 +264,7 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { } } - fn get_drop_flags(&mut self, _path: Self::Path) -> Option> { + fn get_drop_flag(&mut self, _path: Self::Path) -> Option> { None } diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index f08a852510533..26fc4f79aa2fc 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -270,24 +270,18 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { fn array_subpaths(&self, path: Self::Path, size: u64) -> Vec<(Place<'tcx>, Option, Option)> { - if dataflow::move_path_children_matching(self.ctxt.move_data(), path, |_| { - assert!(size <= (u32::MAX as u64), - "move out check doesn't implemented for array bigger then u32"); - true - }).is_none() { + if dataflow::move_path_children_matching(self.ctxt.move_data(), path, + |_| true).is_none() { return vec![]; } + assert!(size <= (u32::MAX as u64), + "move out check doesn't implemented for array bigger then u32"); + let size = size as u32; - let flags = self.ctxt.array_items_drop_flags.get(&path); + let flags = self.ctxt.array_items_drop_flags.get(&path); (0..size).map(|i| { - let place = &self.ctxt.move_data().move_paths[path].place; - (place.clone().elem(ProjectionElem::ConstantIndex{ - offset: i, - min_length: size, - from_end: false - }), - dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| + let move_path = dataflow::move_path_children_matching(self.ctxt.move_data(), path, |p| match p.elem { ProjectionElem::ConstantIndex{offset, min_length: _, from_end: false} => offset == i, @@ -296,8 +290,15 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { ProjectionElem::Subslice{from, to} => from <= i && i < size - to, _ => false } - ), - flags.map(|f| f[i as usize])) + ); + let place = &self.ctxt.move_data().move_paths[path].place; + (place.clone().elem(ProjectionElem::ConstantIndex{ + offset: i, + min_length: size, + from_end: false + }), + move_path, + move_path.and(flags.map(|f| f[i as usize]))) }).collect() } @@ -321,11 +322,11 @@ impl<'a, 'b, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, 'b, 'tcx> { }) } - fn get_drop_flags(&mut self, path: Self::Path) -> Option> { + fn get_drop_flag(&mut self, path: Self::Path) -> Option> { self.ctxt.drop_flag(path).map(|f| match f{ DropFlag::Single(l) => Operand::Copy(Place::Local(*l)), DropFlag::Subslice(_) => - panic!("get_drop_flags shouldn't be calles for sublice move path") + panic!("get_drop_flag shouldn't be calles for sublice move path") }) } } @@ -369,39 +370,52 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { fn create_drop_flag(&mut self, index: MovePathIndex, span: Span) { let tcx = self.tcx; - if let Place::Projection( - box Projection{ref base, elem: ProjectionElem::Subslice{from, to}}) = - self.move_data().move_paths[index].place { - let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx); - if let ty::TyArray(_, n) = base_ty.sty { - let flags = { + + match self.move_data().move_paths[index].place { + Place::Projection(box Projection{ref base, + elem: ProjectionElem::Subslice{from, to}}) => { + let base_ty = base.ty(self.mir, tcx).to_ty(tcx); + if let ty::TyArray(_, n) = base_ty.sty { let n = n.val.to_const_int().and_then(|v| v.to_u64()) .expect("expected u64 size") as usize; - let span = self.mir.span; - let parent_index = self.move_data().move_paths[index].parent - .expect("subslice has parent"); - let patch = &mut self.patch; - let array_flags = self.array_items_drop_flags.entry(parent_index) - .or_insert_with(|| { - let flags = (0..n).map(|_| patch.new_internal(tcx.types.bool, span)) - .collect(); - debug!("create_drop_flags for array with subslice({:?}, {:?}, {:?})", - parent_index, span, flags); - flags - }); let from = from as usize; let to = to as usize; - array_flags[from .. n-to].iter().map(|x| *x).collect() - }; + let flags = self.drop_flags_for_array(n, index)[from .. n-to] + .iter() + .map(|x| *x).collect(); + let span = self.mir.span; + self.drop_flags.entry(index).or_insert_with(|| { + debug!("create_drop_flags for subslice({:?}, {:?}, {:?})", + index, span, flags); + DropFlag::Subslice(flags) + }); + return; + } + } + Place::Projection(box Projection{ref base, + elem: ProjectionElem::ConstantIndex{offset, + min_length: _, + from_end}}) => { + let base_ty = base.ty(self.mir, tcx).to_ty(tcx); + if let ty::TyArray(_, n) = base_ty.sty { + let n = n.val.to_const_int().and_then(|v| v.to_u64()) + .expect("expected u64 size") as usize; + let offset = offset as usize; + let offset = if from_end { n-offset } else { offset }; + let flag = self.drop_flags_for_array(n, index)[offset]; - let span = self.mir.span; - self.drop_flags.entry(index).or_insert_with(|| { - debug!("create_drop_flags for subslice({:?}, {:?}, {:?})", index, span, flags); - DropFlag::Subslice(flags) - }); - return; + let span = self.mir.span; + self.drop_flags.entry(index).or_insert_with(|| { + debug!("create_drop_flags for const index({:?}, {:?}, {:?})", + (offset, from_end), span, flag); + DropFlag::Single(flag) + }); + return; + } } + _ => {} } + let patch = &mut self.patch; debug!("create_drop_flag({:?})", self.mir.span); self.drop_flags.entry(index).or_insert_with(|| { @@ -409,6 +423,22 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { }); } + fn drop_flags_for_array(&mut self, n: usize, index: MovePathIndex) -> &Vec { + let tcx = self.tcx; + let span = self.mir.span; + let parent_index = self.move_data().move_paths[index].parent + .expect("subslice has parent"); + let patch = &mut self.patch; + self.array_items_drop_flags.entry(parent_index) + .or_insert_with(|| { + let flags = (0..n).map(|_| patch.new_internal(tcx.types.bool, span)) + .collect(); + debug!("create_drop_flags for array with subslice({:?}, {:?}, {:?})", + parent_index, span, flags); + flags + }) + } + fn drop_flag(&mut self, index: MovePathIndex) -> Option<&DropFlag> { self.drop_flags.get(&index) } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index 8015558b1a602..c9d0b560f0e0d 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -88,14 +88,13 @@ pub trait DropElaborator<'a, 'tcx: 'a> : fmt::Debug { fn param_env(&self) -> ty::ParamEnv<'tcx>; fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle; - fn get_drop_flags(&mut self, path: Self::Path) -> Option>; + fn get_drop_flag(&mut self, path: Self::Path) -> Option>; fn clear_drop_flag(&mut self, - location: Location, path: - Self::Path, + location: Location, + path: Self::Path, mode: DropFlagMode, opt_drop_flag: Option); - fn field_subpath(&self, path: Self::Path, field: Field) -> Option; fn deref_subpath(&self, path: Self::Path) -> Option; fn downcast_subpath(&self, path: Self::Path, variant: usize) -> Option; @@ -910,7 +909,7 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> DropStyle::Conditional | DropStyle::Open => { let flag = opt_flag .map(|l| Operand::Copy(Place::Local(l))) - .or_else(|| self.elaborator.get_drop_flags(self.path)) + .or_else(|| self.elaborator.get_drop_flag(self.path)) .unwrap(); let term = TerminatorKind::if_(self.tcx(), flag, on_set, on_unset); self.new_block(unwind, term) diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index b165c6099098b..46140c665e3f9 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -243,6 +243,29 @@ fn subslice_pattern_by_arg(a: &Allocator, arg: bool) { } } +fn subslice_pattern_penultimate_by_arg(a: &Allocator, arg: bool) { + let a = [a.alloc(), a.alloc(), a.alloc()]; + if arg { + let[.., _x, _] = a; + } else { + let[_, _y..] = a; + } +} + +fn subslice_pattern_test_with_drop(a: &Allocator, arg: bool, arg2: bool) { + let a = [a.alloc(), a.alloc(), a.alloc(), a.alloc(), a.alloc()]; + if arg2 { + drop(a); + return; + } + + if arg { + let[.., _x, _] = a; + } else { + let[_, _y..] = a; + } +} + fn run_test(mut f: F) where F: FnMut(&Allocator) { @@ -327,5 +350,14 @@ fn main() { run_test(|a| subslice_pattern_by_arg(a, true)); run_test(|a| subslice_pattern_by_arg(a, false)); + run_test(|a| subslice_pattern_penultimate_by_arg(a, true)); + run_test(|a| subslice_pattern_penultimate_by_arg(a, false)); + + + run_test(|a| subslice_pattern_test_with_drop(a, false, false)); + run_test(|a| subslice_pattern_test_with_drop(a, false, true)); + run_test(|a| subslice_pattern_test_with_drop(a, true, false)); + run_test(|a| subslice_pattern_test_with_drop(a, true, true)); + run_test_nopanic(|a| union1(a)); }