]> git.proxmox.com Git - rustc.git/blame - src/test/ui/let-else/let-else-drop-order.rs
Update unsuspicious file list
[rustc.git] / src / test / ui / let-else / let-else-drop-order.rs
CommitLineData
f2b60f7d
FG
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
22use std::cell::RefCell;
23use std::rc::Rc;
24
25#[derive(Clone)]
26struct DropAccountant(Rc<RefCell<Vec<Vec<String>>>>);
27
28impl 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
106fn 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
112struct Droppy<T>(DropAccountant, T);
113
114impl<T> Drop for Droppy<T> {
115 fn drop(&mut self) {
116 self.0.push("drop");
117 }
118}
119
120#[allow(dead_code)]
121enum DroppyEnum<T> {
122 Some(DropAccountant, T),
123 None(DropAccountant),
124}
125
126impl<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
136macro_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
165macro_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
177macro_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
203macro_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
230fn 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}