Auto merge of #33622 - arielb1:elaborate-drops, r=nikomatsakis

[MIR] non-zeroing drop

This enables non-zeroing drop through stack flags for MIR.

Fixes #30380.
Fixes #5016.
This commit is contained in:
bors 2016-06-04 23:49:29 -07:00
commit f97c411548
42 changed files with 2100 additions and 317 deletions

View file

@ -56,5 +56,3 @@ fn main()
//~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0]<bool>
let _: (char, bool) = Trait::without_default_impl_generic(false);
}
//~ TRANS_ITEM drop-glue i8

View file

@ -60,5 +60,3 @@ fn main() {
//~ TRANS_ITEM fn generic_functions::foo3[0]<char, (), ()>
let _ = foo3('v', (), ());
}
//~ TRANS_ITEM drop-glue i8

View file

@ -56,5 +56,3 @@ mod mod2 {
let _ = generic("abc");
}
}
//~ TRANS_ITEM drop-glue i8

View file

@ -0,0 +1,47 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// 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.
// 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);
}
}
#[rustc_mir]
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);
}

View file

@ -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) {

View file

@ -0,0 +1,156 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_attrs)]
use std::cell::{Cell, RefCell};
use std::panic;
use std::usize;
struct InjectedFailure;
struct Allocator {
data: RefCell<Vec<bool>>,
failing_op: usize,
cur_ops: Cell<usize>,
}
impl panic::UnwindSafe for Allocator {}
impl panic::RefUnwindSafe for Allocator {}
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(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);
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
}
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 {
_x = Some(a.alloc());
}
}
#[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();
if c0 {
drop(_v);
}
_v = _w;
if c1 {
_w = a.alloc();
}
}
#[rustc_mir]
fn assignment1(a: &Allocator, c0: bool) {
let mut _v = a.alloc();
let mut _w = a.alloc();
if c0 {
drop(_v);
}
_v = _w;
}
fn run_test<F>(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::<InjectedFailure>().is_none() {
panic::resume_unwind(e);
}
}
}
}
}
fn main() {
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));
}