1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
|
// 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();
}
|