Skip to content

Commit f86e433

Browse files
committed
Add regression tests to ensure stable drop order
1 parent 1e5162c commit f86e433

File tree

1 file changed

+231
-0
lines changed

1 file changed

+231
-0
lines changed
+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
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+
}

0 commit comments

Comments
 (0)