Add tests for super let.

This commit is contained in:
Mara Bos 2025-04-04 11:16:37 +02:00
parent 6c3417dd15
commit ccbef74fc5
2 changed files with 460 additions and 0 deletions

View File

@ -0,0 +1,174 @@
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:30:28
|
LL | super let b = DropMe(&mut x);
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:46:28
|
LL | super let b = &DropMe(&mut x);
| --------------
| | |
| | `x` is borrowed here
| a temporary with access to the borrow is created here ...
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:64:32
|
LL | super let b = identity(&DropMe(&mut x));
| --------------
| | |
| | `x` is borrowed here
| a temporary with access to the borrow is created here ...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | };
| - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:87:36
|
LL | super let b = identity(&DropMe(&mut x));
| --------------
| | |
| | `x` is borrowed here
| a temporary with access to the borrow is created here ...
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | ));
| - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:107:28
|
LL | super let b = DropMe(&mut x);
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:125:28
|
LL | super let b = DropMe(&mut x);
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:143:28
|
LL | super let b = DropMe(&mut x);
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:159:28
|
LL | b = DropMe(&mut x);
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
LL | drop(a);
| - borrow later used here
error[E0716]: temporary value dropped while borrowed
--> $DIR/super-let.rs:172:33
|
LL | #[cfg(borrowck)] { a = &String::from("asdf"); };
| ^^^^^^^^^^^^^^^^^^^^- temporary value is freed at the end of this statement
| |
| creates a temporary value which is freed while still in use
...
LL | let _ = a;
| - borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:206:28
|
LL | super let d = &DropMe(&mut x);
| --------------
| | |
| | `x` is borrowed here
| a temporary with access to the borrow is created here ...
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:227:32
|
LL | super let d = identity(&DropMe(&mut x));
| --------------
| | |
| | `x` is borrowed here
| a temporary with access to the borrow is created here ...
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | };
| - ... and the borrow might be used here, when that temporary is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:246:28
|
LL | super let b = DropMe(&mut x);
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `b` is dropped and runs the `Drop` code for type `DropMe`
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/super-let.rs:263:28
|
LL | let dropme = Some(DropMe(&mut x));
| ------ `x` is borrowed here
...
LL | #[cfg(borrowck)] { x = true; }
| ^^^^^^^^ `x` is assigned to here but it was already borrowed
...
LL | }
| - borrow might be used here, when `x` is dropped and runs the `Drop` code for type `DropMe`
error: aborting due to 13 previous errors
Some errors have detailed explanations: E0506, E0716.
For more information about an error, try `rustc --explain E0506`.

286
tests/ui/super-let.rs Normal file
View File

@ -0,0 +1,286 @@
// 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();
}