Skip to content

Commit 9fd57df

Browse files
committed
add tests, some with incorrect lifetime extension behavior
1 parent a1dbb44 commit 9fd57df

File tree

3 files changed

+157
-0
lines changed

3 files changed

+157
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0716]: temporary value dropped while borrowed
2+
--> $DIR/format-args-temporary-scopes.rs:13:25
3+
|
4+
LL | println!("{:?}", { &temp() });
5+
| ---^^^^^---
6+
| | | |
7+
| | | temporary value is freed at the end of this statement
8+
| | creates a temporary value which is freed while still in use
9+
| borrow later used here
10+
|
11+
= note: consider using a `let` binding to create a longer lived value
12+
13+
error: aborting due to 1 previous error
14+
15+
For more information about this error, try `rustc --explain E0716`.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Test for #145784 as it relates to format arguments: arguments to macros such as `println!`
2+
//! should obey normal temporary scoping rules.
3+
//@ revisions: e2021 e2024
4+
//@ [e2021] check-pass
5+
//@ [e2021] edition: 2021
6+
//@ [e2024] edition: 2024
7+
8+
fn temp() {}
9+
10+
fn main() {
11+
// In Rust 2024, block tail expressions are temporary scopes, so the result of `temp()` is
12+
// dropped after evaluating `&temp()`.
13+
println!("{:?}", { &temp() });
14+
//[e2024]~^ ERROR: temporary value dropped while borrowed [E0716]
15+
16+
// In Rust 1.89, `format_args!` extended the lifetime of all extending expressions in its
17+
// arguments when provided with two or more arguments. This caused the result of `temp()` to
18+
// outlive the result of the block, making this compile.
19+
println!("{:?}{:?}", { &temp() }, ());
20+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//! Test for #145784: the argument to `pin!` should be treated as an extending expression if and
2+
//! only if the whole `pin!` invocation is an extending expression. Likewise, since `pin!` is
3+
//! implemented in terms of `super let`, test the same for `super let` initializers. Since the
4+
//! argument to `pin!` and the initializer of `super let` are not temporary drop scopes, this only
5+
//! affects lifetimes in two cases:
6+
//!
7+
//! - Block tail expressions in Rust 2024, which are both extending expressions and temporary drop
8+
//! scopes; treating them as extending expressions within a non-extending `pin!` resulted in borrow
9+
//! expression operands living past the end of the block.
10+
//!
11+
//! - Nested `super let` statements, which can have their binding and temporary lifetimes extended
12+
//! when the block they're in is an extending expression.
13+
//!
14+
//! For more information on extending expressions, see
15+
//! https://doc.rust-lang.org/reference/destructors.html#extending-based-on-expressions
16+
//!
17+
//! For tests that `super let` initializers aren't temporary drop scopes, and tests for
18+
//! lifetime-extended `super let`, see tests/ui/borrowck/super-let-lifetime-and-drop.rs
19+
//@ run-pass
20+
//@ revisions: e2021 e2024
21+
//@ [e2021] edition: 2021
22+
//@ [e2024] edition: 2024
23+
24+
#![feature(super_let)]
25+
26+
use std::cell::RefCell;
27+
use std::pin::pin;
28+
29+
fn main() {
30+
// Test block arguments to `pin!` in non-extending expressions.
31+
// In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries
32+
// should outlive the `pin!` invocation.
33+
// In Rust 2024, block tail expressions are temporary drop scopes, so their temporaries should
34+
// be dropped after evaluating the tail expression within the `pin!` invocation.
35+
// By nesting two `pin!` calls, this ensures non-extended `pin!` doesn't extend an inner `pin!`.
36+
assert_drop_order(1..=3, |o| {
37+
#[cfg(e2021)]
38+
(
39+
pin!((
40+
pin!({ &o.log(3) as *const LogDrop<'_> }),
41+
drop(o.log(1)),
42+
)),
43+
drop(o.log(2)),
44+
);
45+
#[cfg(e2024)]
46+
(
47+
pin!((
48+
pin!({ &o.log(3) as *const LogDrop<'_> }),
49+
drop(o.log(1)),
50+
)),
51+
drop(o.log(2)),
52+
);
53+
});
54+
55+
// The same holds for `super let` initializers in non-extending expressions.
56+
assert_drop_order(1..=4, |o| {
57+
#[cfg(e2021)]
58+
(
59+
{
60+
super let _ = {
61+
super let _ = { &o.log(4) as *const LogDrop<'_> };
62+
drop(o.log(1))
63+
};
64+
drop(o.log(2))
65+
},
66+
drop(o.log(3)),
67+
);
68+
#[cfg(e2024)]
69+
(
70+
{
71+
super let _ = {
72+
super let _ = { &o.log(4) as *const LogDrop<'_> };
73+
drop(o.log(1))
74+
};
75+
drop(o.log(2))
76+
},
77+
drop(o.log(3)),
78+
);
79+
});
80+
81+
// Within an extending expression, the argument to `pin!` is also an extending expression,
82+
// allowing borrow operands in block tail expressions to have extended lifetimes.
83+
assert_drop_order(1..=2, |o| {
84+
let _ = pin!({ &o.log(2) as *const LogDrop<'_> });
85+
drop(o.log(1));
86+
});
87+
88+
// The same holds for `super let` initializers in extending expressions.
89+
assert_drop_order(1..=2, |o| {
90+
let _ = { super let _ = { &o.log(2) as *const LogDrop<'_> }; };
91+
drop(o.log(1));
92+
});
93+
}
94+
95+
// # Test scaffolding...
96+
97+
struct DropOrder(RefCell<Vec<u64>>);
98+
struct LogDrop<'o>(&'o DropOrder, u64);
99+
100+
impl DropOrder {
101+
fn log(&self, n: u64) -> LogDrop<'_> {
102+
LogDrop(self, n)
103+
}
104+
}
105+
106+
impl<'o> Drop for LogDrop<'o> {
107+
fn drop(&mut self) {
108+
self.0.0.borrow_mut().push(self.1);
109+
}
110+
}
111+
112+
#[track_caller]
113+
fn assert_drop_order(
114+
ex: impl IntoIterator<Item = u64>,
115+
f: impl Fn(&DropOrder),
116+
) {
117+
let order = DropOrder(RefCell::new(Vec::new()));
118+
f(&order);
119+
let order = order.0.into_inner();
120+
let expected: Vec<u64> = ex.into_iter().collect();
121+
assert_eq!(order, expected);
122+
}

0 commit comments

Comments
 (0)