about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-09-04 10:01:53 +1000
committerGitHub <noreply@github.com>2025-09-04 10:01:53 +1000
commitcd59ee791f9a1c8c41a585c3781c4c02c3128c08 (patch)
tree6919c4c08a87da54aeb98cf8ab33560d2c852686 /tests
parent98f6887f359e791b430fb69b7ed68460343167b9 (diff)
parent8fc3938d9961568dfec38b10506f33fd9bc7c147 (diff)
downloadrust-cd59ee791f9a1c8c41a585c3781c4c02c3128c08.tar.gz
rust-cd59ee791f9a1c8c41a585c3781c4c02c3128c08.zip
Rollup merge of #145342 - dianne:if-let-super-let, r=nnethercote
fix drop scope for `super let` bindings within `if let`

Fixes rust-lang/rust#145328 by making non-lifetime-extended `super let` reuse the logic used to compute drop scopes for non-lifetime-extended temporaries.

Also fixes rust-lang/rust#145374, which regressed due to rust-lang/rust#143376 introducing `if let`-like scopes for match arms with guards.

Tracking issue for `super let`: rust-lang/rust#139076

This is a regression fix / breaking change for macros stably exposing `super let`, including `pin!` and `format_args!`.
Nominating to be discussed alongside rust-lang/rust#145328: ```@rustbot``` label +I-lang-nominated +I-libs-api-nominated
Diffstat (limited to 'tests')
-rw-r--r--tests/ui/drop/if-let-super-let.rs112
1 files changed, 112 insertions, 0 deletions
diff --git a/tests/ui/drop/if-let-super-let.rs b/tests/ui/drop/if-let-super-let.rs
new file mode 100644
index 00000000000..c6543e6d3dc
--- /dev/null
+++ b/tests/ui/drop/if-let-super-let.rs
@@ -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(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);
+}