diff options
| author | dianne <diannes.gm@gmail.com> | 2025-07-03 03:31:55 -0700 |
|---|---|---|
| committer | dianne <diannes.gm@gmail.com> | 2025-08-07 16:19:25 -0700 |
| commit | 4ec688110f21b04ab243c5a3e56997ea6c33dbcc (patch) | |
| tree | dc6f99403a33f9e08cf35f80fe430d333e882841 /tests | |
| parent | 2fd855fbfc8239285aa2d596f76a8cc75e17ce02 (diff) | |
| download | rust-4ec688110f21b04ab243c5a3e56997ea6c33dbcc.tar.gz rust-4ec688110f21b04ab243c5a3e56997ea6c33dbcc.zip | |
add more tests for `if let` guard drop order
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/ui/drop/if-let-guards.rs | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/tests/ui/drop/if-let-guards.rs b/tests/ui/drop/if-let-guards.rs new file mode 100644 index 00000000000..918245e029c --- /dev/null +++ b/tests/ui/drop/if-let-guards.rs @@ -0,0 +1,89 @@ +//! Tests drop order for `if let` guard bindings and temporaries. This is for behavior specific to +//! `match` expressions, whereas `tests/ui/drop/drop-order-comparisons.rs` compares `let` chains in +//! guards to `let` chains in `if` expressions. Drop order for `let` chains in guards shouldn't +//! differ between Editions, so we test on both 2021 and 2024, expecting the same results. +//@ revisions: e2021 e2024 +//@ [e2021] edition: 2021 +//@ [e2024] edition: 2024 +//@ run-pass + +#![feature(if_let_guard)] +#![deny(rust_2024_compatibility)] + +use core::{cell::RefCell, ops::Drop}; + +fn main() { + // Test that `let` guard bindings and temps are dropped before the arm's pattern's bindings. + // TODO: this is currently the old behavior (`let` bindings dropped after arm bindings). + assert_drop_order(1..=6, |o| { + // We move out of the scrutinee, so the drop order of the array's elements are based on + // binding declaration order, and they're dropped in the arm's scope. + match [o.log(3), o.log(2)] { + // Partially move from the guard temporary to test drops both for temps and the binding. + [_x, _y] if let [_, _z, _] = [o.log(6), o.log(4), o.log(5)] + && true => { let _a = o.log(1); } + _ => unreachable!(), + } + }); + + // Sanity check: we don't move out of the match scrutinee when the guard fails. + assert_drop_order(1..=4, |o| { + // The scrutinee uses the drop order for arrays since its elements aren't moved. + match [o.log(3), o.log(4)] { + [_x, _y] if let _z = o.log(1) + && false => unreachable!(), + _ => { let _a = o.log(2); } + } + }); + + // Test `let` guards' temporaries are dropped immediately when a guard fails, even if the guard + // is lowered and run multiple times on the same arm due to or-patterns. + assert_drop_order(1..=8, |o| { + let mut _x = 1; + // The match's scrutinee isn't bound by-move, so it outlives the match. + match o.log(8) { + // Failing a guard breaks out of the arm's scope, dropping the `let` guard's scrutinee. + _ | _ | _ if let _ = o.log(_x) + && { _x += 1; false } => unreachable!(), + // The temporaries from a failed guard are dropped before testing the next guard. + _ if let _ = o.log(5) + && { o.push(4); false } => unreachable!(), + // If the guard succeeds, we stay in the arm's scope to execute its body. + _ if let _ = o.log(7) + && true => { o.log(6); } + _ => unreachable!(), + } + }); +} + +// # Test scaffolding... + +struct DropOrder(RefCell<Vec<u64>>); +struct LogDrop<'o>(&'o DropOrder, u64); + +impl DropOrder { + fn log(&self, n: u64) -> LogDrop<'_> { + LogDrop(self, n) + } + fn push(&self, n: u64) { + self.0.borrow_mut().push(n); + } +} + +impl<'o> Drop for LogDrop<'o> { + fn drop(&mut self) { + self.0.push(self.1); + } +} + +#[track_caller] +fn assert_drop_order( + ex: impl IntoIterator<Item = u64>, + f: impl Fn(&DropOrder), +) { + let order = DropOrder(RefCell::new(Vec::new())); + f(&order); + let order = order.0.into_inner(); + let expected: Vec<u64> = ex.into_iter().collect(); + assert_eq!(order, expected); +} |
