add a test with incorrect if let-super let drop order
This commit is contained in:
parent
1553adfe68
commit
bbf08d87eb
1 changed files with 112 additions and 0 deletions
112
tests/ui/drop/if-let-super-let.rs
Normal file
112
tests/ui/drop/if-let-super-let.rs
Normal file
|
|
@ -0,0 +1,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(2); } { o.push(0) },
|
||||
o.push(1),
|
||||
);
|
||||
});
|
||||
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(2); false } {} else { o.push(0) },
|
||||
o.push(1),
|
||||
);
|
||||
});
|
||||
|
||||
// `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(2)) { o.push(0) }, o.push(1));
|
||||
});
|
||||
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(2))) {} else { o.push(0) },
|
||||
o.push(1),
|
||||
);
|
||||
});
|
||||
|
||||
// `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(0)
|
||||
&& let _ = { super let _x = o.log(1); } => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
assert_drop_order(0..=1, |o| {
|
||||
match () {
|
||||
_ if let _ = o.log(0)
|
||||
&& let _ = pin!(o.log(1)) => {}
|
||||
_ => 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);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue