rust/tests/ui/super-let.rs
2025-04-04 16:32:18 +02:00

287 lines
7.2 KiB
Rust

// Check in two ways:
// - borrowck: Check with borrow checking errors when things are alive and dead.
// - runtime: Check with a mutable bool if things are dropped on time.
//
//@ revisions: runtime borrowck
//@ [runtime] run-pass
//@ [borrowck] check-fail
#![allow(dropping_references)]
#![feature(super_let, stmt_expr_attributes)]
use std::convert::identity;
struct DropMe<'a>(&'a mut bool);
impl Drop for DropMe<'_> {
fn drop(&mut self) {
*self.0 = true;
}
}
// Check that a super let variable lives as long as the result of a block.
fn extended_variable() {
let mut x = false;
{
let a = {
super let b = DropMe(&mut x);
&b
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true) // ok
}
// Check that the init expression of a super let is subject to (temporary) lifetime extension.
fn extended_temporary() {
let mut x = false;
{
let a = {
super let b = &DropMe(&mut x);
b
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true); // ok
}
// Check that even non-extended temporaries live until the end of the block,
// but (unlike extended temporaries) not beyond that.
//
// This is necessary for things like select(pin!(identity(&temp()))) to work.
fn non_extended() {
let mut x = false;
{
let _a = {
// Use identity() to supress temporary lifetime extension.
super let b = identity(&DropMe(&mut x));
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
b
// DropMe is still alive here...
};
// ... but not here.
assert_eq!(x, true); // ok
}
}
// Check that even non-extended temporaries live until the end of the block,
// but (unlike extended temporaries) not beyond that.
//
// This is necessary for things like select(pin!(identity(&temp()))) to work.
fn non_extended_in_expression() {
let mut x = false;
{
identity((
{
// Use identity() to supress temporary lifetime extension.
super let b = identity(&DropMe(&mut x));
b
},
{
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
// DropMe is still alive here...
}
));
// ... but not here.
assert_eq!(x, true); // ok
}
}
// Check `super let` in a match arm.
fn match_arm() {
let mut x = false;
{
let a = match Some(123) {
Some(_) => {
super let b = DropMe(&mut x);
&b
}
None => unreachable!(),
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true); // ok
}
// Check `super let` in an if body.
fn if_body() {
let mut x = false;
{
let a = if true {
super let b = DropMe(&mut x);
&b
} else {
unreachable!()
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true); // ok
}
// Check `super let` in an else body.
fn else_body() {
let mut x = false;
{
let a = if false {
unreachable!()
} else {
super let b = DropMe(&mut x);
&b
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true); // ok
}
fn without_initializer() {
let mut x = false;
{
let a = {
super let b;
b = DropMe(&mut x);
b
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true);
}
// Assignment isn't special, even when assigning to a `super let` variable.
fn assignment() {
let mut x = false;
{
super let a;
#[cfg(borrowck)] { a = &String::from("asdf"); }; //[borrowck]~ ERROR dropped while borrowed
#[cfg(runtime)] { a = drop(&DropMe(&mut x)); } // Temporary dropped at the `;` as usual.
assert_eq!(x, true);
let _ = a;
}
}
// `super let mut` should work just fine.
fn mutable() {
let mut x = false;
{
let a = {
super let mut b = None;
&mut b
};
*a = Some(DropMe(&mut x));
}
assert_eq!(x, true);
}
// Temporary lifetime extension should recurse through `super let`s.
fn multiple_levels() {
let mut x = false;
{
let a = {
super let b = {
super let c = {
super let d = &DropMe(&mut x);
d
};
c
};
b
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
drop(a);
// DropMe is still alive here...
}
// ... but not here.
assert_eq!(x, true);
}
// Non-extended temporaries should be dropped at the
// end of the first parent statement that isn't `super`.
fn multiple_levels_but_no_extension() {
let mut x = false;
{
let _a = {
super let b = {
super let c = {
super let d = identity(&DropMe(&mut x));
d
};
c
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
b
// DropMe is still alive here...
};
// ... but not here.
assert_eq!(x, true);
}
}
// Check for potential weird interactions with `let else`.
fn super_let_and_let_else() {
let mut x = false;
{
let a = 'a: {
let Some(_) = Some(123) else { unreachable!() };
super let b = DropMe(&mut x);
let None = Some(123) else { break 'a &b };
unreachable!()
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
// DropMe is still alive here...
drop(a);
}
// ... but not here.
assert_eq!(x, true);
}
// Check if `super let .. else ..;` works.
fn super_let_else() {
let mut x = false;
{
let a = {
let dropme = Some(DropMe(&mut x));
super let Some(x) = dropme else { unreachable!() };
&x
};
#[cfg(borrowck)] { x = true; } //[borrowck]~ ERROR borrowed
// DropMe is still alive here...
drop(a);
}
// ... but not here.
assert_eq!(x, true);
}
fn main() {
extended_variable();
extended_temporary();
non_extended();
non_extended_in_expression();
match_arm();
if_body();
else_body();
without_initializer();
assignment();
mutable();
multiple_levels();
multiple_levels_but_no_extension();
super_let_and_let_else();
super_let_else();
}