// 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>>, val: u32 } impl PushOnDrop { fn new(val: u32, vec: Rc>>) -> 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(); }