Skip to content

Commit c79c50e

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

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

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

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

0 commit comments

Comments
 (0)