|
| 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