]>
Commit | Line | Data |
---|---|---|
041b39d2 XL |
1 | // Copyright 2017 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | #![allow(dead_code, unreachable_code)] | |
12 | ||
13 | use std::cell::RefCell; | |
14 | use std::rc::Rc; | |
15 | use std::panic::{self, AssertUnwindSafe, UnwindSafe}; | |
16 | ||
17 | // This struct is used to record the order in which elements are dropped | |
18 | struct PushOnDrop { | |
19 | vec: Rc<RefCell<Vec<u32>>>, | |
20 | val: u32 | |
21 | } | |
22 | ||
23 | impl PushOnDrop { | |
24 | fn new(val: u32, vec: Rc<RefCell<Vec<u32>>>) -> PushOnDrop { | |
25 | PushOnDrop { vec, val } | |
26 | } | |
27 | } | |
28 | ||
29 | impl Drop for PushOnDrop { | |
30 | fn drop(&mut self) { | |
31 | self.vec.borrow_mut().push(self.val) | |
32 | } | |
33 | } | |
34 | ||
35 | impl UnwindSafe for PushOnDrop { } | |
36 | ||
37 | // Structs | |
38 | struct TestStruct { | |
39 | x: PushOnDrop, | |
40 | y: PushOnDrop, | |
41 | z: PushOnDrop | |
42 | } | |
43 | ||
44 | // Tuple structs | |
45 | struct TestTupleStruct(PushOnDrop, PushOnDrop, PushOnDrop); | |
46 | ||
47 | // Enum variants | |
48 | enum TestEnum { | |
49 | Tuple(PushOnDrop, PushOnDrop, PushOnDrop), | |
50 | Struct { x: PushOnDrop, y: PushOnDrop, z: PushOnDrop } | |
51 | } | |
52 | ||
53 | fn test_drop_tuple() { | |
54 | // Tuple fields are dropped in the same order they are declared | |
55 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
56 | let test_tuple = (PushOnDrop::new(1, dropped_fields.clone()), | |
57 | PushOnDrop::new(2, dropped_fields.clone())); | |
58 | drop(test_tuple); | |
59 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
60 | ||
61 | // Panic during construction means that fields are treated as local variables | |
62 | // Therefore they are dropped in reverse order of initialization | |
63 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
64 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
65 | panic::catch_unwind(|| { | |
66 | (PushOnDrop::new(2, cloned.clone()), | |
67 | PushOnDrop::new(1, cloned.clone()), | |
68 | panic!("this panic is catched :D")); | |
69 | }).err().unwrap(); | |
70 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
71 | } | |
72 | ||
73 | fn test_drop_struct() { | |
74 | // Struct fields are dropped in the same order they are declared | |
75 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
76 | let test_struct = TestStruct { | |
77 | x: PushOnDrop::new(1, dropped_fields.clone()), | |
78 | y: PushOnDrop::new(2, dropped_fields.clone()), | |
79 | z: PushOnDrop::new(3, dropped_fields.clone()), | |
80 | }; | |
81 | drop(test_struct); | |
82 | assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]); | |
83 | ||
84 | // The same holds for tuple structs | |
85 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
86 | let test_tuple_struct = TestTupleStruct(PushOnDrop::new(1, dropped_fields.clone()), | |
87 | PushOnDrop::new(2, dropped_fields.clone()), | |
88 | PushOnDrop::new(3, dropped_fields.clone())); | |
89 | drop(test_tuple_struct); | |
90 | assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]); | |
91 | ||
92 | // Panic during struct construction means that fields are treated as local variables | |
93 | // Therefore they are dropped in reverse order of initialization | |
94 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
95 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
96 | panic::catch_unwind(|| { | |
97 | TestStruct { | |
98 | x: PushOnDrop::new(2, cloned.clone()), | |
99 | y: PushOnDrop::new(1, cloned.clone()), | |
100 | z: panic!("this panic is catched :D") | |
101 | }; | |
102 | }).err().unwrap(); | |
103 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
104 | ||
105 | // Test with different initialization order | |
106 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
107 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
108 | panic::catch_unwind(|| { | |
109 | TestStruct { | |
110 | y: PushOnDrop::new(2, cloned.clone()), | |
111 | x: PushOnDrop::new(1, cloned.clone()), | |
112 | z: panic!("this panic is catched :D") | |
113 | }; | |
114 | }).err().unwrap(); | |
115 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
116 | ||
117 | // The same holds for tuple structs | |
118 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
119 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
120 | panic::catch_unwind(|| { | |
121 | TestTupleStruct(PushOnDrop::new(2, cloned.clone()), | |
122 | PushOnDrop::new(1, cloned.clone()), | |
123 | panic!("this panic is catched :D")); | |
124 | }).err().unwrap(); | |
125 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
126 | } | |
127 | ||
128 | fn test_drop_enum() { | |
129 | // Enum variants are dropped in the same order they are declared | |
130 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
131 | let test_struct_enum = TestEnum::Struct { | |
132 | x: PushOnDrop::new(1, dropped_fields.clone()), | |
133 | y: PushOnDrop::new(2, dropped_fields.clone()), | |
134 | z: PushOnDrop::new(3, dropped_fields.clone()) | |
135 | }; | |
136 | drop(test_struct_enum); | |
137 | assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]); | |
138 | ||
139 | // The same holds for tuple enum variants | |
140 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
141 | let test_tuple_enum = TestEnum::Tuple(PushOnDrop::new(1, dropped_fields.clone()), | |
142 | PushOnDrop::new(2, dropped_fields.clone()), | |
143 | PushOnDrop::new(3, dropped_fields.clone())); | |
144 | drop(test_tuple_enum); | |
145 | assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]); | |
146 | ||
147 | // Panic during enum construction means that fields are treated as local variables | |
148 | // Therefore they are dropped in reverse order of initialization | |
149 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
150 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
151 | panic::catch_unwind(|| { | |
152 | TestEnum::Struct { | |
153 | x: PushOnDrop::new(2, cloned.clone()), | |
154 | y: PushOnDrop::new(1, cloned.clone()), | |
155 | z: panic!("this panic is catched :D") | |
156 | }; | |
157 | }).err().unwrap(); | |
158 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
159 | ||
160 | // Test with different initialization order | |
161 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
162 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
163 | panic::catch_unwind(|| { | |
164 | TestEnum::Struct { | |
165 | y: PushOnDrop::new(2, cloned.clone()), | |
166 | x: PushOnDrop::new(1, cloned.clone()), | |
167 | z: panic!("this panic is catched :D") | |
168 | }; | |
169 | }).err().unwrap(); | |
170 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
171 | ||
172 | // The same holds for tuple enum variants | |
173 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
174 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
175 | panic::catch_unwind(|| { | |
176 | TestEnum::Tuple(PushOnDrop::new(2, cloned.clone()), | |
177 | PushOnDrop::new(1, cloned.clone()), | |
178 | panic!("this panic is catched :D")); | |
179 | }).err().unwrap(); | |
180 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
181 | } | |
182 | ||
183 | fn test_drop_list() { | |
184 | // Elements in a Vec are dropped in the same order they are pushed | |
185 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
186 | let xs = vec![PushOnDrop::new(1, dropped_fields.clone()), | |
187 | PushOnDrop::new(2, dropped_fields.clone()), | |
188 | PushOnDrop::new(3, dropped_fields.clone())]; | |
189 | drop(xs); | |
190 | assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]); | |
191 | ||
192 | // The same holds for arrays | |
193 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
194 | let xs = [PushOnDrop::new(1, dropped_fields.clone()), | |
195 | PushOnDrop::new(2, dropped_fields.clone()), | |
196 | PushOnDrop::new(3, dropped_fields.clone())]; | |
197 | drop(xs); | |
198 | assert_eq!(*dropped_fields.borrow(), &[1, 2, 3]); | |
199 | ||
200 | // Panic during vec construction means that fields are treated as local variables | |
201 | // Therefore they are dropped in reverse order of initialization | |
202 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
203 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
204 | panic::catch_unwind(|| { | |
205 | vec![ | |
206 | PushOnDrop::new(2, cloned.clone()), | |
207 | PushOnDrop::new(1, cloned.clone()), | |
208 | panic!("this panic is catched :D") | |
209 | ]; | |
210 | }).err().unwrap(); | |
211 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
212 | ||
213 | // The same holds for arrays | |
214 | let dropped_fields = Rc::new(RefCell::new(Vec::new())); | |
215 | let cloned = AssertUnwindSafe(dropped_fields.clone()); | |
216 | panic::catch_unwind(|| { | |
217 | [ | |
218 | PushOnDrop::new(2, cloned.clone()), | |
219 | PushOnDrop::new(1, cloned.clone()), | |
220 | panic!("this panic is catched :D") | |
221 | ]; | |
222 | }).err().unwrap(); | |
223 | assert_eq!(*dropped_fields.borrow(), &[1, 2]); | |
224 | } | |
225 | ||
226 | fn main() { | |
227 | test_drop_tuple(); | |
228 | test_drop_struct(); | |
229 | test_drop_enum(); | |
230 | test_drop_list(); | |
231 | } |