//! 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. 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(6), o.log(5)] { // Partially move from the guard temporary to test drops both for temps and the binding. [_x, _y] if let [_, _z, _] = [o.log(4), o.log(2), o.log(3)] && 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>); 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, f: impl Fn(&DropOrder), ) { let order = DropOrder(RefCell::new(Vec::new())); f(&order); let order = order.0.into_inner(); let expected: Vec = ex.into_iter().collect(); assert_eq!(order, expected); }