| //@ 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(); |
| } |