]> git.lizzy.rs Git - rust.git/blob - src/test/ui/issues/issue-23611-enum-swap-in-drop.rs
Rollup merge of #92942 - Xaeroxe:raw_arg, r=dtolnay
[rust.git] / src / test / ui / issues / issue-23611-enum-swap-in-drop.rs
1 // run-pass
2 #![allow(non_upper_case_globals)]
3
4 // Issue 23611: this test is ensuring that, for an instance `X` of the
5 // enum `E`, if you swap in a different variant during the execution
6 // of the `<E as Drop>::drop`, then the appropriate substructure will
7 // be torn down after the `<E as Drop>::drop` method returns.
8
9 use std::cell::RefCell;
10 use std::mem;
11
12 use self::d::D;
13
14 pub fn main() {
15     let log = RefCell::new(vec![]);
16     d::println("created empty log");
17     test(&log);
18
19     // println!("log: {:?}", &log.borrow()[..]);
20     assert_eq!(
21         &log.borrow()[..],
22         [
23             //                                         created empty log
24             // +-- Make D(test_1, 10000000)
25             // | +-- Make D(g_b_5, 50000001)
26             // | |                                     in g_B(b2b0) from E::drop, b=b4b0
27             // | +-- Drop D(g_b_5, 50000001)
28             50000001,
29             // |
30             // | +-- Make D(drop_6, 60000002)
31             // | | +-- Make D(g_b_5, 50000003)
32             // | | |                                   in g_B(b2b0) from E::drop, b=b4b1
33             // | | +-- Drop D(g_b_5, 50000003)
34             50000003,
35             // | |
36             // | | +-- Make D(GaspB::drop_3, 30000004)
37             // | | | +-- Make D(g_b_5, 50000005)
38             // | | | |                                 in g_B(b4b2) from GaspB::drop
39             // | | | +-- Drop D(g_b_5, 50000005)
40             50000005,
41             // | | |
42             // | | +-- Drop D(GaspB::drop_3, 30000004)
43             30000004,
44             // | |
45             // +-- Drop D(test_1, 10000000)
46             10000000,
47             //   |
48             // +-- Make D(GaspA::drop_2, 20000006)
49             // | | +-- Make D(f_a_4, 40000007)
50             // | | |                                   in f_A(a3a0) from GaspA::drop
51             // | | +-- Drop D(f_a_4, 40000007)
52             40000007,
53             // | |
54             // +-- Drop D(GaspA::drop_2, 20000006)
55             20000006,
56             //   |
57             //   +-- Drop D(drop_6, 60000002)
58             60000002
59             //
60                 ]);
61
62     // For reference purposes, the old (incorrect) behavior would produce the following
63     // output, which you can compare to the above:
64     //
65     //                                             created empty log
66     // +-- Make D(test_1, 10000000)
67     // | +-- Make D(g_b_5, 50000001)
68     // | |                                     in g_B(b2b0) from E::drop, b=b4b0
69     // | +-- Drop D(g_b_5, 50000001)
70     // |
71     // | +-- Make D(drop_6, 60000002)
72     // | | +-- Make D(g_b_5, 50000003)
73     // | | |                                   in g_B(b2b0) from E::drop, b=b4b1
74     // | | +-- Drop D(g_b_5, 50000003)
75     // | |
76     // | | +-- Make D(GaspB::drop_3, 30000004)
77     // | | | +-- Make D(g_b_5, 50000005)
78     // | | | |                                 in g_B(b4b2) from GaspB::drop
79     // | | | +-- Drop D(g_b_5, 50000005)
80     // | | |
81     // | | +-- Drop D(GaspB::drop_3, 30000004)
82     // | |
83     // +-- Drop D(test_1, 10000000)
84     //   |
85     // +-- Make D(GaspB::drop_3, 30000006)
86     // | | +-- Make D(f_a_4, 40000007)
87     // | | |                                   in f_A(a3a0) from GaspB::drop
88     // | | +-- Drop D(f_a_4, 40000007)
89     // | |
90     // +-- Drop D(GaspB::drop_3, 30000006)
91     //   |
92     //   +-- Drop D(drop_6, 60000002)
93
94     // Note that this calls f_A from GaspB::drop (and thus creates a D
95     // with a uid incorporating the origin of GaspB's drop that
96     // surrounds the f_A invocation), but the code as written only
97     // ever hands f_A off to instances of GaspA, and thus one should
98     // be able to prove the invariant that f_A is *only* invoked from
99     // from an instance of GaspA (either via the GaspA drop
100     // implementation or the E drop implementaton). Yet the old (bad)
101     // behavior allowed a call to f_A to leak in while we are tearing
102     // down a value of type GaspB.
103 }
104
105 fn test<'a>(log: d::Log<'a>) {
106     let _e = E::B(GaspB(g_b, 0xB4B0, log, D::new("test", 1, log)), true);
107 }
108
109 struct GaspA<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
110 struct GaspB<'a>(for <'b> fn(u32, &'b str, d::Log<'a>), u32, d::Log<'a>, d::D<'a>);
111
112 impl<'a> Drop for GaspA<'a> {
113     fn drop(&mut self) {
114         let _d = d::D::new("GaspA::drop", 2, self.2);
115         (self.0)(self.1, "GaspA::drop", self.2);
116     }
117 }
118
119 impl<'a> Drop for GaspB<'a> {
120     fn drop(&mut self) {
121         let _d = d::D::new("GaspB::drop", 3, self.2);
122         (self.0)(self.1, "GaspB::drop", self.2);
123     }
124 }
125
126 enum E<'a> {
127     A(GaspA<'a>, bool), B(GaspB<'a>, bool),
128 }
129
130 fn f_a(x: u32, ctxt: &str, log: d::Log) {
131     let _d = d::D::new("f_a", 4, log);
132     d::println(&format!("in f_A({:x}) from {}", x, ctxt));
133 }
134 fn g_b(y: u32, ctxt: &str, log: d::Log) {
135     let _d = d::D::new("g_b", 5, log);
136     d::println(&format!("in g_B({:x}) from {}", y, ctxt));
137 }
138
139 impl<'a> Drop for E<'a> {
140     fn drop(&mut self) {
141         let (do_drop, log) = match *self {
142             E::A(GaspA(ref f, ref mut val_a, log, ref _d_a), ref mut do_drop) => {
143                 f(0xA1A0, &format!("E::drop, a={:x}", val_a), log);
144                 *val_a += 1;
145                 // swap in do_drop := false to avoid infinite dtor regress
146                 (mem::replace(do_drop, false), log)
147             }
148             E::B(GaspB(ref g, ref mut val_b, log, ref _d_b), ref mut do_drop) => {
149                 g(0xB2B0, &format!("E::drop, b={:x}", val_b), log);
150                 *val_b += 1;
151                 // swap in do_drop := false to avoid infinite dtor regress
152                 (mem::replace(do_drop, false), log)
153             }
154         };
155
156         #[allow(unused_must_use)]
157         if do_drop {
158             mem::replace(self, E::A(GaspA(f_a, 0xA3A0, log, D::new("drop", 6, log)), true));
159         }
160     }
161 }
162
163 // This module provides simultaneous printouts of the dynamic extents
164 // of all of the D values, in addition to logging the order that each
165 // is dropped.
166 //
167 // This code is similar to a support code module embedded within
168 // test/run-pass/issue-123338-ensure-param-drop-order.rs; however,
169 // that (slightly simpler) code only identifies objects in the log via
170 // (creation) time-stamps; this incorporates both timestamping and the
171 // point of origin within the source code into the unique ID (uid).
172
173 const PREF_INDENT: u32 = 20;
174
175 pub mod d {
176     #![allow(unused_parens)]
177     use std::fmt;
178     use std::mem;
179     use std::cell::RefCell;
180
181     static mut counter: u16 = 0;
182     static mut trails: u64 = 0;
183
184     pub type Log<'a> = &'a RefCell<Vec<u32>>;
185
186     pub fn current_width() -> u32 {
187         unsafe { max_width() - trails.leading_zeros() }
188     }
189
190     pub fn max_width() -> u32 {
191         unsafe {
192             (mem::size_of_val(&trails)*8) as u32
193         }
194     }
195
196     pub fn indent_println(my_trails: u32, s: &str) {
197         let mut indent: String = String::new();
198         for i in 0..my_trails {
199             unsafe {
200                 if trails & (1 << i) != 0 {
201                     indent = indent + "| ";
202                 } else {
203                     indent = indent + "  ";
204                 }
205             }
206         }
207         println!("{}{}", indent, s);
208     }
209
210     pub fn println(s: &str) {
211         indent_println(super::PREF_INDENT, s);
212     }
213
214     fn first_avail() -> u32 {
215         unsafe {
216             for i in 0..64 {
217                 if trails & (1 << i) == 0 {
218                     return i;
219                 }
220             }
221         }
222         panic!("exhausted trails");
223     }
224
225     pub struct D<'a> {
226         name: &'static str, i: u8, uid: u32, trail: u32, log: Log<'a>
227     }
228
229     impl<'a> fmt::Display for D<'a> {
230         fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
231             write!(w, "D({}_{}, {})", self.name, self.i, self.uid)
232         }
233     }
234
235     impl<'a> D<'a> {
236         pub fn new(name: &'static str, i: u8, log: Log<'a>) -> D<'a> {
237             unsafe {
238                 let trail = first_avail();
239                 let ctr = ((i as u32) * 10_000_000) + (counter as u32);
240                 counter += 1;
241                 trails |= (1 << trail);
242                 let ret = D {
243                     name: name, i: i, log: log, uid: ctr, trail: trail
244                 };
245                 indent_println(trail, &format!("+-- Make {}", ret));
246                 ret
247             }
248         }
249     }
250
251     impl<'a> Drop for D<'a> {
252         fn drop(&mut self) {
253             unsafe { trails &= !(1 << self.trail); };
254             self.log.borrow_mut().push(self.uid);
255             indent_println(self.trail, &format!("+-- Drop {}", self));
256             indent_println(::PREF_INDENT, "");
257         }
258     }
259 }