summaryrefslogtreecommitdiffstats
path: root/src/test/ui/rfcs/rfc1857-drop-order.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/ui/rfcs/rfc1857-drop-order.rs')
-rw-r--r--src/test/ui/rfcs/rfc1857-drop-order.rs225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/test/ui/rfcs/rfc1857-drop-order.rs b/src/test/ui/rfcs/rfc1857-drop-order.rs
new file mode 100644
index 000000000..243b7fb6f
--- /dev/null
+++ b/src/test/ui/rfcs/rfc1857-drop-order.rs
@@ -0,0 +1,225 @@
+// run-pass
+// needs-unwind
+// ignore-wasm32-bare compiled with panic=abort by default
+
+#![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();
+}