225 lines
8.1 KiB
Rust
225 lines
8.1 KiB
Rust
//@ run-pass
|
|
//@ needs-unwind
|
|
|
|
#![allow(dead_code, unreachable_code)]
|
|
|
|
use std::cell::RefCell;
|
|
use std::rc::Rc;
|
|
use std::panic::{self, AssertUnwindSafe, UnwindSafe};
|
|
|
|
// This struct is used to record the order in which elements are dropped
|
|
struct PushOnDrop {
|
|
vec: Rc<RefCell<Vec<u32>>>,
|
|
val: u32
|
|
}
|
|
|
|
impl PushOnDrop {
|
|
fn new(val: u32, vec: Rc<RefCell<Vec<u32>>>) -> PushOnDrop {
|
|
PushOnDrop { vec, val }
|
|
}
|
|
}
|
|
|
|
impl Drop for PushOnDrop {
|
|
fn drop(&mut self) {
|
|
self.vec.borrow_mut().push(self.val)
|
|
}
|
|
}
|
|
|
|
impl UnwindSafe for PushOnDrop { }
|
|
|
|
// Structs
|
|
struct TestStruct {
|
|
x: PushOnDrop,
|
|
y: PushOnDrop,
|
|
z: PushOnDrop
|
|
}
|
|
|
|
// Tuple structs
|
|
struct TestTupleStruct(PushOnDrop, PushOnDrop, PushOnDrop);
|
|
|
|
// Enum variants
|
|
enum TestEnum {
|
|
Tuple(PushOnDrop, PushOnDrop, PushOnDrop),
|
|
Struct { x: PushOnDrop, y: PushOnDrop, z: PushOnDrop }
|
|
}
|
|
|
|
fn test_drop_tuple() {
|
|
// Tuple fields are dropped in the same order they are declared
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let test_tuple = (PushOnDrop::new(1, dropped_fields.clone()),
|
|
PushOnDrop::new(2, dropped_fields.clone()));
|
|
drop(test_tuple);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
|
|
// Panic during construction means that fields are treated as local variables
|
|
// Therefore they are dropped in reverse order of initialization
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
(PushOnDrop::new(2, cloned.clone()),
|
|
PushOnDrop::new(1, cloned.clone()),
|
|
panic!("this panic is caught :D"));
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
}
|
|
|
|
fn test_drop_struct() {
|
|
// Struct fields are dropped in the same order they are declared
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let test_struct = TestStruct {
|
|
x: PushOnDrop::new(1, dropped_fields.clone()),
|
|
y: PushOnDrop::new(2, dropped_fields.clone()),
|
|
z: PushOnDrop::new(3, dropped_fields.clone()),
|
|
};
|
|
drop(test_struct);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]);
|
|
|
|
// The same holds for tuple structs
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let test_tuple_struct = TestTupleStruct(PushOnDrop::new(1, dropped_fields.clone()),
|
|
PushOnDrop::new(2, dropped_fields.clone()),
|
|
PushOnDrop::new(3, dropped_fields.clone()));
|
|
drop(test_tuple_struct);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]);
|
|
|
|
// Panic during struct construction means that fields are treated as local variables
|
|
// Therefore they are dropped in reverse order of initialization
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
TestStruct {
|
|
x: PushOnDrop::new(2, cloned.clone()),
|
|
y: PushOnDrop::new(1, cloned.clone()),
|
|
z: panic!("this panic is caught :D")
|
|
};
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
|
|
// Test with different initialization order
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
TestStruct {
|
|
y: PushOnDrop::new(2, cloned.clone()),
|
|
x: PushOnDrop::new(1, cloned.clone()),
|
|
z: panic!("this panic is caught :D")
|
|
};
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
|
|
// The same holds for tuple structs
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
TestTupleStruct(PushOnDrop::new(2, cloned.clone()),
|
|
PushOnDrop::new(1, cloned.clone()),
|
|
panic!("this panic is caught :D"));
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
}
|
|
|
|
fn test_drop_enum() {
|
|
// Enum variants are dropped in the same order they are declared
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let test_struct_enum = TestEnum::Struct {
|
|
x: PushOnDrop::new(1, dropped_fields.clone()),
|
|
y: PushOnDrop::new(2, dropped_fields.clone()),
|
|
z: PushOnDrop::new(3, dropped_fields.clone())
|
|
};
|
|
drop(test_struct_enum);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]);
|
|
|
|
// The same holds for tuple enum variants
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let test_tuple_enum = TestEnum::Tuple(PushOnDrop::new(1, dropped_fields.clone()),
|
|
PushOnDrop::new(2, dropped_fields.clone()),
|
|
PushOnDrop::new(3, dropped_fields.clone()));
|
|
drop(test_tuple_enum);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]);
|
|
|
|
// Panic during enum construction means that fields are treated as local variables
|
|
// Therefore they are dropped in reverse order of initialization
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
TestEnum::Struct {
|
|
x: PushOnDrop::new(2, cloned.clone()),
|
|
y: PushOnDrop::new(1, cloned.clone()),
|
|
z: panic!("this panic is caught :D")
|
|
};
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
|
|
// Test with different initialization order
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
TestEnum::Struct {
|
|
y: PushOnDrop::new(2, cloned.clone()),
|
|
x: PushOnDrop::new(1, cloned.clone()),
|
|
z: panic!("this panic is caught :D")
|
|
};
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
|
|
// The same holds for tuple enum variants
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
TestEnum::Tuple(PushOnDrop::new(2, cloned.clone()),
|
|
PushOnDrop::new(1, cloned.clone()),
|
|
panic!("this panic is caught :D"));
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
}
|
|
|
|
fn test_drop_list() {
|
|
// Elements in a Vec are dropped in the same order they are pushed
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let xs = vec![PushOnDrop::new(1, dropped_fields.clone()),
|
|
PushOnDrop::new(2, dropped_fields.clone()),
|
|
PushOnDrop::new(3, dropped_fields.clone())];
|
|
drop(xs);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]);
|
|
|
|
// The same holds for arrays
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let xs = [PushOnDrop::new(1, dropped_fields.clone()),
|
|
PushOnDrop::new(2, dropped_fields.clone()),
|
|
PushOnDrop::new(3, dropped_fields.clone())];
|
|
drop(xs);
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]);
|
|
|
|
// Panic during vec construction means that fields are treated as local variables
|
|
// Therefore they are dropped in reverse order of initialization
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
vec![
|
|
PushOnDrop::new(2, cloned.clone()),
|
|
PushOnDrop::new(1, cloned.clone()),
|
|
panic!("this panic is caught :D")
|
|
];
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
|
|
// The same holds for arrays
|
|
let dropped_fields = Rc::new(RefCell::new(Vec::new()));
|
|
let cloned = AssertUnwindSafe(dropped_fields.clone());
|
|
panic::catch_unwind(|| {
|
|
[
|
|
PushOnDrop::new(2, cloned.clone()),
|
|
PushOnDrop::new(1, cloned.clone()),
|
|
panic!("this panic is caught :D")
|
|
];
|
|
}).err().unwrap();
|
|
assert_eq!(*dropped_fields.borrow(), &[1, 2]);
|
|
}
|
|
|
|
fn main() {
|
|
test_drop_tuple();
|
|
test_drop_struct();
|
|
test_drop_enum();
|
|
test_drop_list();
|
|
}
|