Skip to content

Commit 9da2aac

Browse files
committed
translate array drop glue using MIR
This fixes leakage on panic with arrays & slices. I am using a C-style for-loop instead of a pointer-based loop because that would be ugly-er to implement.
1 parent 5d2512e commit 9da2aac

File tree

5 files changed

+152
-46
lines changed

5 files changed

+152
-46
lines changed

src/librustc/ty/util.rs

+9
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,15 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
584584
bug!("empty_substs_for_def_id: {:?} has type parameters", item_def_id)
585585
})
586586
}
587+
588+
pub fn const_usize(&self, val: usize) -> ConstInt {
589+
match self.sess.target.uint_type {
590+
ast::UintTy::U16 => ConstInt::Usize(ConstUsize::Us16(val as u16)),
591+
ast::UintTy::U32 => ConstInt::Usize(ConstUsize::Us32(val as u32)),
592+
ast::UintTy::U64 => ConstInt::Usize(ConstUsize::Us64(val as u64)),
593+
_ => bug!(),
594+
}
595+
}
587596
}
588597

589598
pub struct TypeIdHasher<'a, 'gcx: 'a+'tcx, 'tcx: 'a, W> {

src/librustc_mir/util/elaborate_drops.rs

+130-5
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use std::fmt;
1212
use rustc::hir;
1313
use rustc::mir::*;
14-
use rustc::middle::const_val::ConstInt;
14+
use rustc::middle::const_val::{ConstInt, ConstVal};
1515
use rustc::middle::lang_items;
1616
use rustc::ty::{self, Ty};
1717
use rustc::ty::subst::{Kind, Substs};
@@ -535,6 +535,114 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
535535
})
536536
}
537537

538+
/// create a loop that drops an array:
539+
///
540+
/// loop-block:
541+
/// can_go = index < len
542+
/// if can_go then drop-block else succ
543+
/// drop-block:
544+
/// ptr = &mut LV[len]
545+
/// index = index + 1
546+
/// drop(ptr)
547+
fn drop_loop(&mut self,
548+
unwind: Option<BasicBlock>,
549+
succ: BasicBlock,
550+
index: &Lvalue<'tcx>,
551+
length: &Lvalue<'tcx>,
552+
ety: Ty<'tcx>,
553+
is_cleanup: bool)
554+
-> BasicBlock
555+
{
556+
let use_ = |lv: &Lvalue<'tcx>| Operand::Consume(lv.clone());
557+
let tcx = self.tcx();
558+
559+
let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut {
560+
ty: ety,
561+
mutbl: hir::Mutability::MutMutable
562+
});
563+
let ptr = &Lvalue::Local(self.new_temp(ref_ty));
564+
let can_go = &Lvalue::Local(self.new_temp(tcx.types.bool));
565+
566+
let one = self.constant_usize(1);
567+
let drop_block = self.elaborator.patch().new_block(BasicBlockData {
568+
statements: vec![
569+
Statement { source_info: self.source_info, kind: StatementKind::Assign(
570+
ptr.clone(), Rvalue::Ref(
571+
tcx.types.re_erased, BorrowKind::Mut,
572+
self.lvalue.clone().index(use_(index))
573+
),
574+
)},
575+
Statement { source_info: self.source_info, kind: StatementKind::Assign(
576+
index.clone(), Rvalue::BinaryOp(BinOp::Add, use_(index), one)
577+
)},
578+
],
579+
is_cleanup,
580+
terminator: Some(Terminator {
581+
source_info: self.source_info,
582+
kind: TerminatorKind::Resume,
583+
})
584+
});
585+
586+
let loop_block = self.elaborator.patch().new_block(BasicBlockData {
587+
statements: vec![
588+
Statement { source_info: self.source_info, kind: StatementKind::Assign(
589+
can_go.clone(), Rvalue::BinaryOp(BinOp::Lt, use_(index), use_(length))
590+
)},
591+
],
592+
is_cleanup,
593+
terminator: Some(Terminator {
594+
source_info: self.source_info,
595+
kind: TerminatorKind::if_(tcx, use_(can_go), drop_block, succ)
596+
})
597+
});
598+
599+
self.elaborator.patch().patch_terminator(drop_block, TerminatorKind::Drop {
600+
location: ptr.clone().deref(),
601+
target: loop_block,
602+
unwind: unwind
603+
});
604+
605+
loop_block
606+
}
607+
608+
fn open_drop_for_array(&mut self, ety: Ty<'tcx>) -> BasicBlock {
609+
debug!("open_drop_for_array({:?})", ety);
610+
// FIXME: using an index instead of a pointer to avoid
611+
// special-casing ZSTs.
612+
let tcx = self.tcx();
613+
let index = &Lvalue::Local(self.new_temp(tcx.types.usize));
614+
let length = &Lvalue::Local(self.new_temp(tcx.types.usize));
615+
616+
let unwind = self.unwind.map(|unwind| {
617+
self.drop_loop(None, unwind, index, length, ety, true)
618+
});
619+
620+
let is_cleanup = self.is_cleanup;
621+
let succ = self.succ; // FIXME(#6393)
622+
let loop_block = self.drop_loop(unwind, succ, index, length, ety, is_cleanup);
623+
624+
let zero = self.constant_usize(0);
625+
let drop_block = self.elaborator.patch().new_block(BasicBlockData {
626+
statements: vec![
627+
Statement { source_info: self.source_info, kind: StatementKind::Assign(
628+
length.clone(), Rvalue::Len(self.lvalue.clone())
629+
)},
630+
Statement { source_info: self.source_info, kind: StatementKind::Assign(
631+
index.clone(), Rvalue::Use(zero),
632+
)},
633+
],
634+
is_cleanup,
635+
terminator: Some(Terminator {
636+
source_info: self.source_info,
637+
kind: TerminatorKind::Goto { target: loop_block }
638+
})
639+
});
640+
641+
// FIXME(#34708): handle partially-dropped array/slice elements.
642+
self.drop_flag_test_and_reset_block(
643+
is_cleanup, Some(DropFlagMode::Deep), drop_block, succ)
644+
}
645+
538646
/// The slow-path - create an "open", elaborated drop for a type
539647
/// which is moved-out-of only partially, and patch `bb` to a jump
540648
/// to it. This must not be called on ADTs with a destructor,
@@ -564,10 +672,8 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
564672
ty::TyDynamic(..) => {
565673
self.complete_drop(is_cleanup, Some(DropFlagMode::Deep), succ)
566674
}
567-
ty::TyArray(..) | ty::TySlice(..) => {
568-
// FIXME(#34708): handle partially-dropped
569-
// array/slice elements.
570-
self.complete_drop(is_cleanup, Some(DropFlagMode::Deep), succ)
675+
ty::TyArray(ety, _) | ty::TySlice(ety) => {
676+
self.open_drop_for_array(ety)
571677
}
572678
_ => bug!("open drop from non-ADT `{:?}`", ty)
573679
}
@@ -588,6 +694,17 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
588694
debug!("complete_drop({:?},{:?})", self, drop_mode);
589695

590696
let drop_block = self.drop_block(is_cleanup, succ);
697+
self.drop_flag_test_and_reset_block(is_cleanup, drop_mode, drop_block, succ)
698+
}
699+
700+
fn drop_flag_test_and_reset_block(&mut self,
701+
is_cleanup: bool,
702+
drop_mode: Option<DropFlagMode>,
703+
drop_block: BasicBlock,
704+
succ: BasicBlock) -> BasicBlock
705+
{
706+
debug!("drop_flag_test_and_reset_block({:?},{:?})", self, drop_mode);
707+
591708
if let Some(mode) = drop_mode {
592709
let block_start = Location { block: drop_block, statement_index: 0 };
593710
self.elaborator.clear_drop_flag(block_start, self.path, mode);
@@ -691,4 +808,12 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D>
691808
let mir = self.elaborator.mir();
692809
self.elaborator.patch().terminator_loc(mir, bb)
693810
}
811+
812+
fn constant_usize(&self, val: usize) -> Operand<'tcx> {
813+
Operand::Constant(box Constant {
814+
span: self.source_info.span,
815+
ty: self.tcx().types.usize,
816+
literal: Literal::Value { value: ConstVal::Integral(self.tcx().const_usize(val)) }
817+
})
818+
}
694819
}

src/librustc_trans/collector.rs

+1-11
Original file line numberDiff line numberDiff line change
@@ -612,17 +612,7 @@ fn visit_instance_use<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
612612
output.push(create_fn_trans_item(instance));
613613
}
614614
}
615-
ty::InstanceDef::DropGlue(_, Some(ty)) => {
616-
match ty.sty {
617-
ty::TyArray(ety, _) |
618-
ty::TySlice(ety)
619-
if is_direct_call =>
620-
{
621-
// drop of arrays/slices is translated in-line.
622-
visit_drop_use(scx, ety, false, output);
623-
}
624-
_ => {}
625-
};
615+
ty::InstanceDef::DropGlue(_, Some(_)) => {
626616
output.push(create_fn_trans_item(instance));
627617
}
628618
ty::InstanceDef::ClosureOnceShim { .. } |

src/librustc_trans/mir/block.rs

+1-30
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@ use base::{self, Lifetime};
2020
use callee;
2121
use builder::Builder;
2222
use common::{self, Funclet};
23-
use common::{C_bool, C_str_slice, C_struct, C_u32, C_uint, C_undef};
23+
use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
2424
use consts;
2525
use machine::llalign_of_min;
2626
use meth;
2727
use monomorphize;
2828
use type_of;
29-
use tvec;
3029
use type_::Type;
3130

3231
use rustc_data_structures::indexed_vec::IndexVec;
@@ -222,34 +221,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
222221
let (drop_fn, need_extra) = match ty.sty {
223222
ty::TyDynamic(..) => (meth::DESTRUCTOR.get_fn(&bcx, lvalue.llextra),
224223
false),
225-
ty::TyArray(ety, _) | ty::TySlice(ety) => {
226-
// FIXME: handle panics
227-
let drop_fn = monomorphize::resolve_drop_in_place(
228-
bcx.ccx.shared(), ety);
229-
let drop_fn = callee::get_fn(bcx.ccx, drop_fn);
230-
let bcx = tvec::slice_for_each(
231-
&bcx,
232-
lvalue.project_index(&bcx, C_uint(bcx.ccx, 0u64)),
233-
ety,
234-
lvalue.len(bcx.ccx),
235-
|bcx, llval, loop_bb| {
236-
self.set_debug_loc(&bcx, terminator.source_info);
237-
if let Some(unwind) = unwind {
238-
bcx.invoke(
239-
drop_fn,
240-
&[llval],
241-
loop_bb,
242-
llblock(self, unwind),
243-
cleanup_bundle
244-
);
245-
} else {
246-
bcx.call(drop_fn, &[llval], cleanup_bundle);
247-
bcx.br(loop_bb);
248-
}
249-
});
250-
funclet_br(self, bcx, target);
251-
return
252-
}
253224
_ => (callee::get_fn(bcx.ccx, drop_fn), lvalue.has_extra())
254225
};
255226
let args = &[lvalue.llval, lvalue.llextra][..1 + need_extra as usize];

src/test/run-pass/dynamic-drop.rs

+11
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,14 @@ fn union1(a: &Allocator) {
125125
}
126126
}
127127

128+
fn array_simple(a: &Allocator) {
129+
let _x = [a.alloc(), a.alloc(), a.alloc(), a.alloc()];
130+
}
131+
132+
fn vec_simple(a: &Allocator) {
133+
let _x = vec![a.alloc(), a.alloc(), a.alloc(), a.alloc()];
134+
}
135+
128136
fn run_test<F>(mut f: F)
129137
where F: FnMut(&Allocator)
130138
{
@@ -171,5 +179,8 @@ fn main() {
171179
run_test(|a| assignment1(a, false));
172180
run_test(|a| assignment1(a, true));
173181

182+
run_test(|a| array_simple(a));
183+
run_test(|a| vec_simple(a));
184+
174185
run_test_nopanic(|a| union1(a));
175186
}

0 commit comments

Comments
 (0)