about summary refs log tree commit diff
diff options
context:
space:
mode:
authordianne <diannes.gm@gmail.com>2025-08-12 23:55:29 -0700
committerdianne <diannes.gm@gmail.com>2025-08-25 19:35:26 -0700
commit9fd57df6397b0e2b0cd4db32884ed5e5443c7906 (patch)
treeaaf48ab12c353d183417ca05259b1045ebff757a
parenta1dbb443527bd126452875eb5d5860c1d001d761 (diff)
downloadrust-9fd57df6397b0e2b0cd4db32884ed5e5443c7906.tar.gz
rust-9fd57df6397b0e2b0cd4db32884ed5e5443c7906.zip
add tests, some with incorrect lifetime extension behavior
-rw-r--r--tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr15
-rw-r--r--tests/ui/borrowck/format-args-temporary-scopes.rs20
-rw-r--r--tests/ui/drop/super-let-tail-expr-drop-order.rs122
3 files changed, 157 insertions, 0 deletions
diff --git a/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr
new file mode 100644
index 00000000000..923c929a68d
--- /dev/null
+++ b/tests/ui/borrowck/format-args-temporary-scopes.e2024.stderr
@@ -0,0 +1,15 @@
+error[E0716]: temporary value dropped while borrowed
+  --> $DIR/format-args-temporary-scopes.rs:13:25
+   |
+LL |     println!("{:?}", { &temp() });
+   |                      ---^^^^^---
+   |                      |  |    |
+   |                      |  |    temporary value is freed at the end of this statement
+   |                      |  creates a temporary value which is freed while still in use
+   |                      borrow later used here
+   |
+   = note: consider using a `let` binding to create a longer lived value
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0716`.
diff --git a/tests/ui/borrowck/format-args-temporary-scopes.rs b/tests/ui/borrowck/format-args-temporary-scopes.rs
new file mode 100644
index 00000000000..e6223d7c551
--- /dev/null
+++ b/tests/ui/borrowck/format-args-temporary-scopes.rs
@@ -0,0 +1,20 @@
+//! Test for #145784 as it relates to format arguments: arguments to macros such as `println!`
+//! should obey normal temporary scoping rules.
+//@ revisions: e2021 e2024
+//@ [e2021] check-pass
+//@ [e2021] edition: 2021
+//@ [e2024] edition: 2024
+
+fn temp() {}
+
+fn main() {
+    // In Rust 2024, block tail expressions are temporary scopes, so the result of `temp()` is
+    // dropped after evaluating `&temp()`.
+    println!("{:?}", { &temp() });
+    //[e2024]~^ ERROR: temporary value dropped while borrowed [E0716]
+
+    // In Rust 1.89, `format_args!` extended the lifetime of all extending expressions in its
+    // arguments when provided with two or more arguments. This caused the result of `temp()` to
+    // outlive the result of the block, making this compile.
+    println!("{:?}{:?}", { &temp() }, ());
+}
diff --git a/tests/ui/drop/super-let-tail-expr-drop-order.rs b/tests/ui/drop/super-let-tail-expr-drop-order.rs
new file mode 100644
index 00000000000..5aa17bba21f
--- /dev/null
+++ b/tests/ui/drop/super-let-tail-expr-drop-order.rs
@@ -0,0 +1,122 @@
+//! Test for #145784: the argument to `pin!` should be treated as an extending expression if and
+//! only if the whole `pin!` invocation is an extending expression. Likewise, since `pin!` is
+//! implemented in terms of `super let`, test the same for `super let` initializers. Since the
+//! argument to `pin!` and the initializer of `super let` are not temporary drop scopes, this only
+//! affects lifetimes in two cases:
+//!
+//! - Block tail expressions in Rust 2024, which are both extending expressions and temporary drop
+//! scopes; treating them as extending expressions within a non-extending `pin!` resulted in borrow
+//! expression operands living past the end of the block.
+//!
+//! - Nested `super let` statements, which can have their binding and temporary lifetimes extended
+//! when the block they're in is an extending expression.
+//!
+//! For more information on extending expressions, see
+//! https://doc.rust-lang.org/reference/destructors.html#extending-based-on-expressions
+//!
+//! For tests that `super let` initializers aren't temporary drop scopes, and tests for
+//! lifetime-extended `super let`, see tests/ui/borrowck/super-let-lifetime-and-drop.rs
+//@ run-pass
+//@ revisions: e2021 e2024
+//@ [e2021] edition: 2021
+//@ [e2024] edition: 2024
+
+#![feature(super_let)]
+
+use std::cell::RefCell;
+use std::pin::pin;
+
+fn main() {
+    // Test block arguments to `pin!` in non-extending expressions.
+    // In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries
+    // should outlive the `pin!` invocation.
+    // In Rust 2024, block tail expressions are temporary drop scopes, so their temporaries should
+    // be dropped after evaluating the tail expression within the `pin!` invocation.
+    // By nesting two `pin!` calls, this ensures non-extended `pin!` doesn't extend an inner `pin!`.
+    assert_drop_order(1..=3, |o| {
+        #[cfg(e2021)]
+        (
+            pin!((
+                pin!({ &o.log(3) as *const LogDrop<'_> }),
+                drop(o.log(1)),
+            )),
+            drop(o.log(2)),
+        );
+        #[cfg(e2024)]
+        (
+            pin!((
+                pin!({ &o.log(3) as *const LogDrop<'_> }),
+                drop(o.log(1)),
+            )),
+            drop(o.log(2)),
+        );
+    });
+
+    // The same holds for `super let` initializers in non-extending expressions.
+    assert_drop_order(1..=4, |o| {
+        #[cfg(e2021)]
+        (
+            {
+                super let _ = {
+                    super let _ = { &o.log(4) as *const LogDrop<'_> };
+                    drop(o.log(1))
+                };
+                drop(o.log(2))
+            },
+            drop(o.log(3)),
+        );
+        #[cfg(e2024)]
+        (
+            {
+                super let _ = {
+                    super let _ = { &o.log(4) as *const LogDrop<'_> };
+                    drop(o.log(1))
+                };
+                drop(o.log(2))
+            },
+            drop(o.log(3)),
+        );
+    });
+
+    // Within an extending expression, the argument to `pin!` is also an extending expression,
+    // allowing borrow operands in block tail expressions to have extended lifetimes.
+    assert_drop_order(1..=2, |o| {
+        let _ = pin!({ &o.log(2) as *const LogDrop<'_> });
+        drop(o.log(1));
+    });
+
+    // The same holds for `super let` initializers in extending expressions.
+    assert_drop_order(1..=2, |o| {
+        let _ =  { super let _ = { &o.log(2) as *const LogDrop<'_> }; };
+        drop(o.log(1));
+    });
+}
+
+// # Test scaffolding...
+
+struct DropOrder(RefCell<Vec<u64>>);
+struct LogDrop<'o>(&'o DropOrder, u64);
+
+impl DropOrder {
+    fn log(&self, n: u64) -> LogDrop<'_> {
+        LogDrop(self, n)
+    }
+}
+
+impl<'o> Drop for LogDrop<'o> {
+    fn drop(&mut self) {
+        self.0.0.borrow_mut().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);
+}