about summary refs log tree commit diff
path: root/tests/ui/drop/if-let-guards.rs
blob: bd353112c09bc29e5cc37ce9fd5f7d9a90a16069 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//! 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<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);
}