Skip to content

Commit 68feb6c

Browse files
committed
add a test with incorrect if let-super let drop order
1 parent 1553adf commit 68feb6c

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

tests/ui/drop/if-let-super-let.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
//! Test for #145328: ensure the lifetime of a `super let` binding within an `if let` scrutinee is
2+
//! at most the scope of the `if` condition. In particular, test `pin!` since it's implemented in
3+
//! terms of `super let`.
4+
//@ run-pass
5+
//@ revisions: e2021 e2024
6+
//@ [e2021] edition: 2021
7+
//@ [e2024] edition: 2024
8+
9+
#![feature(if_let_guard)]
10+
#![feature(super_let)]
11+
#![expect(irrefutable_let_patterns)]
12+
13+
use std::cell::RefCell;
14+
use std::pin::pin;
15+
16+
fn main() {
17+
// The `super let` bindings here should have the same scope as `if let` temporaries.
18+
// In Rust 2021, this means it lives past the end of the `if` expression.
19+
// In Rust 2024, this means it lives to the end of the `if`'s success block.
20+
assert_drop_order(0..=2, |o| {
21+
#[cfg(e2021)]
22+
(
23+
if let _ = { super let _x = o.log(2); } { o.push(0) },
24+
o.push(1),
25+
);
26+
#[cfg(e2024)]
27+
(
28+
if let _ = { super let _x = o.log(2); } { o.push(0) },
29+
o.push(1),
30+
);
31+
});
32+
assert_drop_order(0..=2, |o| {
33+
#[cfg(e2021)]
34+
(
35+
if let true = { super let _x = o.log(2); false } {} else { o.push(0) },
36+
o.push(1),
37+
);
38+
#[cfg(e2024)]
39+
(
40+
if let true = { super let _x = o.log(2); false } {} else { o.push(0) },
41+
o.push(1),
42+
);
43+
});
44+
45+
// `pin!` should behave likewise.
46+
assert_drop_order(0..=2, |o| {
47+
#[cfg(e2021)] (if let _ = pin!(o.log(2)) { o.push(0) }, o.push(1));
48+
#[cfg(e2024)] (if let _ = pin!(o.log(2)) { o.push(0) }, o.push(1));
49+
});
50+
assert_drop_order(0..=2, |o| {
51+
#[cfg(e2021)]
52+
(
53+
if let None = Some(pin!(o.log(2))) {} else { o.push(0) },
54+
o.push(1),
55+
);
56+
#[cfg(e2024)]
57+
(
58+
if let None = Some(pin!(o.log(2))) {} else { o.push(0) },
59+
o.push(1),
60+
);
61+
});
62+
63+
// `super let` bindings' scope should also be consistent with `if let` temporaries in guards.
64+
// Here, that means the `super let` binding in the second guard condition operand should be
65+
// dropped before the first operand's temporary. This is consistent across Editions.
66+
assert_drop_order(0..=1, |o| {
67+
match () {
68+
_ if let _ = o.log(0)
69+
&& let _ = { super let _x = o.log(1); } => {}
70+
_ => unreachable!(),
71+
}
72+
});
73+
assert_drop_order(0..=1, |o| {
74+
match () {
75+
_ if let _ = o.log(0)
76+
&& let _ = pin!(o.log(1)) => {}
77+
_ => unreachable!(),
78+
}
79+
});
80+
}
81+
82+
// # Test scaffolding...
83+
84+
struct DropOrder(RefCell<Vec<u64>>);
85+
struct LogDrop<'o>(&'o DropOrder, u64);
86+
87+
impl DropOrder {
88+
fn log(&self, n: u64) -> LogDrop<'_> {
89+
LogDrop(self, n)
90+
}
91+
fn push(&self, n: u64) {
92+
self.0.borrow_mut().push(n);
93+
}
94+
}
95+
96+
impl<'o> Drop for LogDrop<'o> {
97+
fn drop(&mut self) {
98+
self.0.push(self.1);
99+
}
100+
}
101+
102+
#[track_caller]
103+
fn assert_drop_order(
104+
ex: impl IntoIterator<Item = u64>,
105+
f: impl Fn(&DropOrder),
106+
) {
107+
let order = DropOrder(RefCell::new(Vec::new()));
108+
f(&order);
109+
let order = order.0.into_inner();
110+
let expected: Vec<u64> = ex.into_iter().collect();
111+
assert_eq!(order, expected);
112+
}

0 commit comments

Comments
 (0)