// 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(); }