Skip to content

Commit 26c9343

Browse files
committed
add more tests for if let guard drop order
1 parent 9e64506 commit 26c9343

File tree

1 file changed

+88
-0
lines changed

1 file changed

+88
-0
lines changed

tests/ui/drop/if-let-guards.rs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//! Tests drop order for `if let` guard bindings and temporaries. This is for behavior specific to
2+
//! `match` expressions, whereas `tests/ui/drop/drop-order-comparisons.rs` compares `let` chains in
3+
//! guards to `let` chains in `if` expressions.
4+
//@ revisions: e2021 e2024
5+
//@ [e2021] edition: 2021
6+
//@ [e2024] edition: 2024
7+
//@ run-pass
8+
9+
#![feature(if_let_guard)]
10+
#![deny(rust_2024_compatibility)]
11+
12+
use core::{cell::RefCell, ops::Drop};
13+
14+
fn main() {
15+
// Test that `let` guard bindings and temps are dropped before the arm's pattern's bindings.
16+
// TODO: this is currently the old behavior (`let` bindings dropped after arm bindings).
17+
assert_drop_order(1..=6, |o| {
18+
// We move out of the scrutinee, so the drop order of the array's elements are based on
19+
// binding declaration order, and they're dropped in the arm's scope.
20+
match [o.log(3), o.log(2)] {
21+
// Partially move from the guard temporary to test drops both for temps and the binding.
22+
[_x, _y] if let [_, _z, _] = [o.log(6), o.log(4), o.log(5)]
23+
&& true => { let _a = o.log(1); }
24+
_ => unreachable!(),
25+
}
26+
});
27+
28+
// Sanity check: we don't move out of the match scrutinee when the guard fails.
29+
assert_drop_order(1..=4, |o| {
30+
// The scrutinee uses the drop order for arrays since its elements aren't moved.
31+
match [o.log(3), o.log(4)] {
32+
[_x, _y] if let _z = o.log(1)
33+
&& false => unreachable!(),
34+
_ => { let _a = o.log(2); }
35+
}
36+
});
37+
38+
// Test `let` guards' temporaries are dropped immediately when a guard fails, even if the guard
39+
// is lowered and run multiple times on the same arm due to or-patterns.
40+
assert_drop_order(1..=8, |o| {
41+
let mut _x = 1;
42+
// The match's scrutinee isn't bound by-move, so it's kept until the end of the match.
43+
match o.log(8) {
44+
// Failing a guard breaks out of the arm's scope, dropping the `let` guard's scrutinee.
45+
_ | _ | _ if let _ = o.log(_x)
46+
&& { _x += 1; false } => unreachable!(),
47+
// The temporaries from a failed guard are dropped before testing the next guard.
48+
_ if let _ = o.log(5)
49+
&& { o.push(4); false } => unreachable!(),
50+
// If the guard succeeds, we stay in the arm's scope to execute its body.
51+
_ if let _ = o.log(7)
52+
&& true => { o.log(6); }
53+
_ => unreachable!(),
54+
}
55+
});
56+
}
57+
58+
// # Test scaffolding...
59+
60+
struct DropOrder(RefCell<Vec<u64>>);
61+
struct LogDrop<'o>(&'o DropOrder, u64);
62+
63+
impl DropOrder {
64+
fn log(&self, n: u64) -> LogDrop<'_> {
65+
LogDrop(self, n)
66+
}
67+
fn push(&self, n: u64) {
68+
self.0.borrow_mut().push(n);
69+
}
70+
}
71+
72+
impl<'o> Drop for LogDrop<'o> {
73+
fn drop(&mut self) {
74+
self.0.push(self.1);
75+
}
76+
}
77+
78+
#[track_caller]
79+
fn assert_drop_order(
80+
ex: impl IntoIterator<Item = u64>,
81+
f: impl Fn(&DropOrder),
82+
) {
83+
let order = DropOrder(RefCell::new(Vec::new()));
84+
f(&order);
85+
let order = order.0.into_inner();
86+
let expected: Vec<u64> = ex.into_iter().collect();
87+
assert_eq!(order, expected);
88+
}

0 commit comments

Comments
 (0)