File: or-pattern-drop-order.rs

package info (click to toggle)
rustc 1.89.0%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 906,624 kB
  • sloc: xml: 158,148; python: 34,888; javascript: 19,595; sh: 19,221; ansic: 13,046; cpp: 7,144; asm: 4,376; makefile: 692; lisp: 174; sql: 15
file content (109 lines) | stat: -rw-r--r-- 4,830 bytes parent folder | download | duplicates (4)
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//@ run-pass
//! Test drop order for different ways of declaring pattern bindings involving or-patterns.
//! Currently, it's inconsistent between language constructs (#142163).

use std::cell::RefCell;
use std::ops::Drop;

// For more informative failures, we collect drops in a `Vec` before checking their order.
struct DropOrder(RefCell<Vec<u32>>);
struct LogDrop<'o>(&'o DropOrder, u32);

impl<'o> Drop for LogDrop<'o> {
    fn drop(&mut self) {
        self.0.0.borrow_mut().push(self.1);
    }
}

#[track_caller]
fn assert_drop_order(expected_drops: impl IntoIterator<Item = u32>, f: impl Fn(&DropOrder)) {
    let order = DropOrder(RefCell::new(Vec::new()));
    f(&order);
    let order = order.0.into_inner();
    let correct_order: Vec<u32> = expected_drops.into_iter().collect();
    assert_eq!(order, correct_order);
}

#[expect(unused_variables, unused_assignments, irrefutable_let_patterns)]
fn main() {
    // When bindings are declared with `let pat;`, they're visited in left-to-right order, using the
    // order given by the first occurrence of each variable. They're later dropped in reverse.
    assert_drop_order(1..=3, |o| {
        // Drops are right-to-left: `z`, `y`, `x`.
        let (x, Ok(y) | Err(y), z);
        // Assignment order doesn't matter.
        z = LogDrop(o, 1);
        y = LogDrop(o, 2);
        x = LogDrop(o, 3);
    });
    assert_drop_order(1..=2, |o| {
        // The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
        let ((true, x, y) | (false, y, x));
        x = LogDrop(o, 2);
        y = LogDrop(o, 1);
    });

    // When bindings are declared with `let pat = expr;`, bindings within or-patterns are seen last,
    // thus they're dropped first.
    assert_drop_order(1..=3, |o| {
        // Drops are right-to-left, treating `y` as rightmost: `y`, `z`, `x`.
        let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2));
    });
    assert_drop_order(1..=2, |o| {
        // The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
        let ((true, x, y) | (false, y, x)) = (true, LogDrop(o, 2), LogDrop(o, 1));
    });
    assert_drop_order(1..=2, |o| {
        // That drop order is used regardless of which or-pattern alternative matches: `y`, `x`.
        let ((true, x, y) | (false, y, x)) = (false, LogDrop(o, 1), LogDrop(o, 2));
    });

    // `match` treats or-patterns as last like `let pat = expr;`, but also determines drop order
    // using the order of the bindings in the *last* or-pattern alternative.
    assert_drop_order(1..=3, |o| {
        // Drops are right-to-left, treating `y` as rightmost: `y`, `z`, `x`.
        match (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) { (x, Ok(y) | Err(y), z) => {} }
    });
    assert_drop_order(1..=2, |o| {
        // The last or-pattern alternative determines the bindings' drop order: `x`, `y`.
        match (true, LogDrop(o, 1), LogDrop(o, 2)) { (true, x, y) | (false, y, x) => {} }
    });
    assert_drop_order(1..=2, |o| {
        // That drop order is used regardless of which or-pattern alternative matches: `x`, `y`.
        match (false, LogDrop(o, 2), LogDrop(o, 1)) { (true, x, y) | (false, y, x) => {} }
    });

    // Function params are visited one-by-one, and the order of bindings within a param's pattern is
    // the same as `let pat = expr`;
    assert_drop_order(1..=3, |o| {
        // Among separate params, the drop order is right-to-left: `z`, `y`, `x`.
        (|x, (Ok(y) | Err(y)), z| {})(LogDrop(o, 3), Ok(LogDrop(o, 2)), LogDrop(o, 1));
    });
    assert_drop_order(1..=3, |o| {
        // Within a param's pattern, or-patterns are treated as rightmost: `y`, `z`, `x`.
        (|(x, Ok(y) | Err(y), z)| {})((LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)));
    });
    assert_drop_order(1..=2, |o| {
        // The first or-pattern alternative determines the bindings' drop order: `y`, `x`.
        (|((true, x, y) | (false, y, x))| {})((true, LogDrop(o, 2), LogDrop(o, 1)));
    });

    // `if let` and `let`-`else` see bindings in the same order as `let pat = expr;`.
    // Vars in or-patterns are seen last (dropped first), and the first alternative's order is used.
    assert_drop_order(1..=3, |o| {
        if let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) {}
    });
    assert_drop_order(1..=3, |o| {
        let (x, Ok(y) | Err(y), z) = (LogDrop(o, 3), Ok(LogDrop(o, 1)), LogDrop(o, 2)) else {
            unreachable!();
        };
    });
    assert_drop_order(1..=2, |o| {
        if let (true, x, y) | (false, y, x) = (true, LogDrop(o, 2), LogDrop(o, 1)) {}
    });
    assert_drop_order(1..=2, |o| {
        let ((true, x, y) | (false, y, x)) = (true, LogDrop(o, 2), LogDrop(o, 1)) else {
            unreachable!();
        };
    });
}