mirror of
https://github.com/rust-lang/rust.git
synced 2025-10-02 10:18:25 +00:00

They now use the enclosing temporary scope as their scope, regardless of which `ScopeData` was used to mark it.
113 lines
3.2 KiB
Rust
113 lines
3.2 KiB
Rust
//! 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);
|
|
}
|