about summary refs log tree commit diff
path: root/tests/ui/drop/if-let-super-let.rs
blob: c6543e6d3dc978cc5a498de4c8b4264661ed3b89 (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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Test for #145328: ensure the lifetime of a `super let` binding within an `if let` scrutinee is
//! at most the scope of the `if` condition's temporaries. Additionally, test `pin!` since it's
//! implemented in terms of `super let` and exposes this behavior.
//@ run-pass
//@ revisions: e2021 e2024
//@ [e2021] edition: 2021
//@ [e2024] edition: 2024

#![feature(if_let_guard)]
#![feature(super_let)]
#![expect(irrefutable_let_patterns)]

use std::cell::RefCell;
use std::pin::pin;

fn main() {
    // The `super let` bindings here should have the same scope as `if let` temporaries.
    // In Rust 2021, this means it lives past the end of the `if` expression.
    // In Rust 2024, this means it lives to the end of the `if`'s success block.
    assert_drop_order(0..=2, |o| {
        #[cfg(e2021)]
        (
            if let _ = { super let _x = o.log(2); } { o.push(0) },
            o.push(1),
        );
        #[cfg(e2024)]
        (
            if let _ = { super let _x = o.log(1); } { o.push(0) },
            o.push(2),
        );
    });
    assert_drop_order(0..=2, |o| {
        #[cfg(e2021)]
        (
            if let true = { super let _x = o.log(2); false } {} else { o.push(0) },
            o.push(1),
        );
        #[cfg(e2024)]
        (
            if let true = { super let _x = o.log(0); false } {} else { o.push(1) },
            o.push(2),
        );
    });

    // `pin!` should behave likewise.
    assert_drop_order(0..=2, |o| {
        #[cfg(e2021)] (if let _ = pin!(o.log(2)) { o.push(0) }, o.push(1));
        #[cfg(e2024)] (if let _ = pin!(o.log(1)) { o.push(0) }, o.push(2));
    });
    assert_drop_order(0..=2, |o| {
        #[cfg(e2021)]
        (
            if let None = Some(pin!(o.log(2))) {} else { o.push(0) },
            o.push(1),
        );
        #[cfg(e2024)]
        (
            if let None = Some(pin!(o.log(0))) {} else { o.push(1) },
            o.push(2),
        );
    });

    // `super let` bindings' scope should also be consistent with `if let` temporaries in guards.
    // Here, that means the `super let` binding in the second guard condition operand should be
    // dropped before the first operand's temporary. This is consistent across Editions.
    assert_drop_order(0..=1, |o| {
        match () {
            _ if let _ = o.log(1)
                && let _ = { super let _x = o.log(0); } => {}
            _ => unreachable!(),
        }
    });
    assert_drop_order(0..=1, |o| {
        match () {
            _ if let _ = o.log(1)
                && let _ = pin!(o.log(0)) => {}
            _ => 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);
}