]> git.lizzy.rs Git - rust.git/blob - tests/ui/let-else/let-else-drop-order.rs
Don't resolve type var roots in point_at_expr_source_of_inferred_type
[rust.git] / tests / ui / let-else / let-else-drop-order.rs
1 // run-pass
2 // edition:2021
3 // check-run-results
4 //
5 // Drop order tests for let else
6 //
7 // Mostly this ensures two things:
8 // 1. That let and let else temporary drop order is the same.
9 //    This is a specific design request: https://github.com/rust-lang/rust/pull/93628#issuecomment-1047140316
10 // 2. That the else block truly only runs after the
11 //    temporaries have dropped.
12 //
13 // We also print some nice tables for an overview by humans.
14 // Changes in those tables are considered breakages, but the
15 // important properties 1 and 2 are also enforced by the code.
16 // This is important as it's easy to update the stdout file
17 // with a --bless and miss the impact of that change.
18
19
20 #![allow(irrefutable_let_patterns)]
21
22 use std::cell::RefCell;
23 use std::rc::Rc;
24
25 #[derive(Clone)]
26 struct DropAccountant(Rc<RefCell<Vec<Vec<String>>>>);
27
28 impl DropAccountant {
29     fn new() -> Self {
30         Self(Default::default())
31     }
32     fn build_droppy(&self, v: u32) -> Droppy<u32> {
33         Droppy(self.clone(), v)
34     }
35     fn build_droppy_enum_none(&self, _v: u32) -> ((), DroppyEnum<u32>) {
36         ((), DroppyEnum::None(self.clone()))
37     }
38     fn new_list(&self, s: impl ToString) {
39         self.0.borrow_mut().push(vec![s.to_string()]);
40     }
41     fn push(&self, s: impl ToString) {
42         let s = s.to_string();
43         let mut accounts = self.0.borrow_mut();
44         accounts.last_mut().unwrap().push(s);
45     }
46     fn print_table(&self) {
47         println!();
48
49         let accounts = self.0.borrow();
50         let before_last = &accounts[accounts.len() - 2];
51         let last = &accounts[accounts.len() - 1];
52         let before_last = get_comma_list(before_last);
53         let last = get_comma_list(last);
54         const LINES: &[&str] = &[
55             "vanilla",
56             "&",
57             "&mut",
58             "move",
59             "fn(this)",
60             "tuple",
61             "array",
62             "ref &",
63             "ref mut &mut",
64         ];
65         let max_len = LINES.iter().map(|v| v.len()).max().unwrap();
66         let max_len_before = before_last.iter().map(|v| v.len()).max().unwrap();
67         let max_len_last = last.iter().map(|v| v.len()).max().unwrap();
68
69         println!(
70             "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
71             "construct", before_last[0], last[0]
72         );
73         println!("| {:-<max_len$} | {:-<max_len_before$} | {:-<max_len_last$} |", "", "", "");
74
75         for ((l, l_before), l_last) in
76             LINES.iter().zip(before_last[1..].iter()).zip(last[1..].iter())
77         {
78             println!(
79                 "| {: <max_len$} | {: <max_len_before$} | {: <max_len_last$} |",
80                 l, l_before, l_last,
81             );
82         }
83     }
84     #[track_caller]
85     fn assert_all_equal_to(&self, st: &str) {
86         let accounts = self.0.borrow();
87         let last = &accounts[accounts.len() - 1];
88         let last = get_comma_list(last);
89         for line in last[1..].iter() {
90             assert_eq!(line.trim(), st.trim());
91         }
92     }
93     #[track_caller]
94     fn assert_equality_last_two_lists(&self) {
95         let accounts = self.0.borrow();
96         let last = &accounts[accounts.len() - 1];
97         let before_last = &accounts[accounts.len() - 2];
98         for (l, b) in last[1..].iter().zip(before_last[1..].iter()) {
99             if !(l == b || l == "n/a" || b == "n/a") {
100                 panic!("not equal: '{last:?}' != '{before_last:?}'");
101             }
102         }
103     }
104 }
105
106 fn get_comma_list(sl: &[String]) -> Vec<String> {
107     std::iter::once(sl[0].clone())
108         .chain(sl[1..].chunks(2).map(|c| c.join(",")))
109         .collect::<Vec<String>>()
110 }
111
112 struct Droppy<T>(DropAccountant, T);
113
114 impl<T> Drop for Droppy<T> {
115     fn drop(&mut self) {
116         self.0.push("drop");
117     }
118 }
119
120 #[allow(dead_code)]
121 enum DroppyEnum<T> {
122     Some(DropAccountant, T),
123     None(DropAccountant),
124 }
125
126 impl<T> Drop for DroppyEnum<T> {
127     fn drop(&mut self) {
128         match self {
129             DroppyEnum::Some(acc, _inner) => acc,
130             DroppyEnum::None(acc) => acc,
131         }
132         .push("drop");
133     }
134 }
135
136 macro_rules! nestings_with {
137     ($construct:ident, $binding:pat, $exp:expr) => {
138         // vanilla:
139         $construct!($binding, $exp.1);
140
141         // &:
142         $construct!(&$binding, &$exp.1);
143
144         // &mut:
145         $construct!(&mut $binding, &mut ($exp.1));
146
147         {
148             // move:
149             let w = $exp;
150             $construct!(
151                 $binding,
152                 {
153                     let w = w;
154                     w
155                 }
156                 .1
157             );
158         }
159
160         // fn(this):
161         $construct!($binding, std::convert::identity($exp).1);
162     };
163 }
164
165 macro_rules! nestings {
166     ($construct:ident, $binding:pat, $exp:expr) => {
167         nestings_with!($construct, $binding, $exp);
168
169         // tuple:
170         $construct!(($binding, 77), ($exp.1, 77));
171
172         // array:
173         $construct!([$binding], [$exp.1]);
174     };
175 }
176
177 macro_rules! let_else {
178     ($acc:expr, $v:expr, $binding:pat, $build:ident) => {
179         let acc = $acc;
180         let v = $v;
181
182         macro_rules! let_else_construct {
183             ($arg:pat, $exp:expr) => {
184                 loop {
185                     let $arg = $exp else {
186                         acc.push("else");
187                         break;
188                     };
189                     acc.push("body");
190                     break;
191                 }
192             };
193         }
194         nestings!(let_else_construct, $binding, acc.$build(v));
195         // ref &:
196         let_else_construct!($binding, &acc.$build(v).1);
197
198         // ref mut &mut:
199         let_else_construct!($binding, &mut acc.$build(v).1);
200     };
201 }
202
203 macro_rules! let_ {
204     ($acc:expr, $binding:tt) => {
205         let acc = $acc;
206
207         macro_rules! let_construct {
208             ($arg:pat, $exp:expr) => {{
209                 let $arg = $exp;
210                 acc.push("body");
211             }};
212         }
213         let v = 0;
214         {
215             nestings_with!(let_construct, $binding, acc.build_droppy(v));
216         }
217         acc.push("n/a");
218         acc.push("n/a");
219         acc.push("n/a");
220         acc.push("n/a");
221
222         // ref &:
223         let_construct!($binding, &acc.build_droppy(v).1);
224
225         // ref mut &mut:
226         let_construct!($binding, &mut acc.build_droppy(v).1);
227     };
228 }
229
230 fn main() {
231     let acc = DropAccountant::new();
232
233     println!(" --- matching cases ---");
234
235     // Ensure that let and let else have the same behaviour
236     acc.new_list("let _");
237     let_!(&acc, _);
238     acc.new_list("let else _");
239     let_else!(&acc, 0, _, build_droppy);
240     acc.assert_equality_last_two_lists();
241     acc.print_table();
242
243     // Ensure that let and let else have the same behaviour
244     acc.new_list("let _v");
245     let_!(&acc, _v);
246     acc.new_list("let else _v");
247     let_else!(&acc, 0, _v, build_droppy);
248     acc.assert_equality_last_two_lists();
249     acc.print_table();
250
251     println!();
252
253     println!(" --- mismatching cases ---");
254
255     acc.new_list("let else _ mismatch");
256     let_else!(&acc, 1, DroppyEnum::Some(_, _), build_droppy_enum_none);
257     acc.new_list("let else _v mismatch");
258     let_else!(&acc, 1, DroppyEnum::Some(_, _v), build_droppy_enum_none);
259     acc.print_table();
260     // This ensures that we always drop before visiting the else case
261     acc.assert_all_equal_to("drop,else");
262
263     acc.new_list("let else 0 mismatch");
264     let_else!(&acc, 1, 0, build_droppy);
265     acc.new_list("let else 0 mismatch");
266     let_else!(&acc, 1, 0, build_droppy);
267     acc.print_table();
268     // This ensures that we always drop before visiting the else case
269     acc.assert_all_equal_to("drop,else");
270 }