Skip to content

Commit 9fbd2e6

Browse files
committed
add a test with incorrect lifetime extension behavior
1 parent a1dbb44 commit 9fbd2e6

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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(if_let_guard)]
25+
#![feature(super_let)]
26+
27+
use std::cell::RefCell;
28+
use std::pin::pin;
29+
30+
fn main() {
31+
// Test block arguments to `pin!` in non-extending expressions.
32+
// In Rust 2021, block tail expressions aren't temporary drop scopes, so their temporaries
33+
// should outlive the `pin!` invocation.
34+
// In Rust 2024, block tail expressions are temporary drop scopes, so their temporaries should
35+
// be dropped after evaluating the tail expression within the `pin!` invocation.
36+
// By nesting two `pin!` calls, this ensures non-extended `pin!` doesn't extend an inner `pin!`.
37+
assert_drop_order(1..=3, |o| {
38+
#[cfg(e2021)]
39+
(
40+
pin!((
41+
pin!({ &o.log(3) as *const LogDrop<'_> }),
42+
drop(o.log(1)),
43+
)),
44+
drop(o.log(2)),
45+
);
46+
#[cfg(e2024)]
47+
(
48+
pin!((
49+
pin!({ &o.log(3) as *const LogDrop<'_> }),
50+
drop(o.log(1)),
51+
)),
52+
drop(o.log(2)),
53+
);
54+
});
55+
56+
// The same holds for `super let` initializers in non-extending expressions.
57+
assert_drop_order(1..=4, |o| {
58+
#[cfg(e2021)]
59+
(
60+
{
61+
super let _ = {
62+
super let _ = { &o.log(4) as *const LogDrop<'_> };
63+
drop(o.log(1))
64+
};
65+
drop(o.log(2))
66+
},
67+
drop(o.log(3)),
68+
);
69+
#[cfg(e2024)]
70+
(
71+
{
72+
super let _ = {
73+
super let _ = { &o.log(4) as *const LogDrop<'_> };
74+
drop(o.log(1))
75+
};
76+
drop(o.log(2))
77+
},
78+
drop(o.log(3)),
79+
);
80+
});
81+
82+
// Within an extending expression, the argument to `pin!` is also an extending expression,
83+
// allowing borrow operands in block tail expressions to have extended lifetimes.
84+
assert_drop_order(1..=2, |o| {
85+
let _ = pin!({ &o.log(2) as *const LogDrop<'_> });
86+
drop(o.log(1));
87+
});
88+
89+
// The same holds for `super let` initializers in extending expressions.
90+
assert_drop_order(1..=2, |o| {
91+
let _ = { super let _ = { &o.log(2) as *const LogDrop<'_> }; };
92+
drop(o.log(1));
93+
});
94+
}
95+
96+
// # Test scaffolding...
97+
98+
struct DropOrder(RefCell<Vec<u64>>);
99+
struct LogDrop<'o>(&'o DropOrder, u64);
100+
101+
impl DropOrder {
102+
fn log(&self, n: u64) -> LogDrop<'_> {
103+
LogDrop(self, n)
104+
}
105+
}
106+
107+
impl<'o> Drop for LogDrop<'o> {
108+
fn drop(&mut self) {
109+
self.0.0.borrow_mut().push(self.1);
110+
}
111+
}
112+
113+
#[track_caller]
114+
fn assert_drop_order(
115+
ex: impl IntoIterator<Item = u64>,
116+
f: impl Fn(&DropOrder),
117+
) {
118+
let order = DropOrder(RefCell::new(Vec::new()));
119+
f(&order);
120+
let order = order.0.into_inner();
121+
let expected: Vec<u64> = ex.into_iter().collect();
122+
assert_eq!(order, expected);
123+
}

0 commit comments

Comments
 (0)