tail expression behind terminating scope
This commit is contained in:
parent
11380368dc
commit
0f8c3f7882
@ -1716,24 +1716,28 @@ fn lower_expr_for(
|
||||
// `mut iter => { ... }`
|
||||
let iter_arm = self.arm(iter_pat, loop_expr);
|
||||
|
||||
let into_iter_expr = match loop_kind {
|
||||
let match_expr = match loop_kind {
|
||||
ForLoopKind::For => {
|
||||
// `::std::iter::IntoIterator::into_iter(<head>)`
|
||||
self.expr_call_lang_item_fn(
|
||||
let into_iter_expr = self.expr_call_lang_item_fn(
|
||||
head_span,
|
||||
hir::LangItem::IntoIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
)
|
||||
}
|
||||
// ` unsafe { Pin::new_unchecked(&mut into_async_iter(<head>)) }`
|
||||
ForLoopKind::ForAwait => {
|
||||
// `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
|
||||
let iter = self.expr_call_lang_item_fn(
|
||||
head_span,
|
||||
hir::LangItem::IntoAsyncIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
);
|
||||
let iter = self.expr_mut_addr_of(head_span, iter);
|
||||
|
||||
self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
into_iter_expr,
|
||||
arena_vec![self; iter_arm],
|
||||
hir::MatchSource::ForLoopDesugar,
|
||||
))
|
||||
}
|
||||
// `match into_async_iter(<head>) { ref mut iter => match unsafe { Pin::new_unchecked(iter) } { ... } }`
|
||||
ForLoopKind::ForAwait => {
|
||||
let iter_ident = iter;
|
||||
let (async_iter_pat, async_iter_pat_id) =
|
||||
self.pat_ident_binding_mode(head_span, iter_ident, hir::BindingMode::REF_MUT);
|
||||
let iter = self.expr_ident_mut(head_span, iter_ident, async_iter_pat_id);
|
||||
// `Pin::new_unchecked(...)`
|
||||
let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut(
|
||||
head_span,
|
||||
@ -1742,17 +1746,29 @@ fn lower_expr_for(
|
||||
));
|
||||
// `unsafe { ... }`
|
||||
let iter = self.arena.alloc(self.expr_unsafe(iter));
|
||||
iter
|
||||
}
|
||||
};
|
||||
|
||||
let match_expr = self.arena.alloc(self.expr_match(
|
||||
let inner_match_expr = self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
into_iter_expr,
|
||||
iter,
|
||||
arena_vec![self; iter_arm],
|
||||
hir::MatchSource::ForLoopDesugar,
|
||||
));
|
||||
|
||||
// `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
|
||||
let iter = self.expr_call_lang_item_fn(
|
||||
head_span,
|
||||
hir::LangItem::IntoAsyncIterIntoIter,
|
||||
arena_vec![self; head],
|
||||
);
|
||||
let iter_arm = self.arm(async_iter_pat, inner_match_expr);
|
||||
self.arena.alloc(self.expr_match(
|
||||
for_span,
|
||||
iter,
|
||||
arena_vec![self; iter_arm],
|
||||
hir::MatchSource::ForLoopDesugar,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// This is effectively `{ let _result = ...; _result }`.
|
||||
// The construct was introduced in #21984 and is necessary to make sure that
|
||||
// temporaries in the `head` expression are dropped and do not leak to the
|
||||
|
@ -588,6 +588,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(incomplete, return_type_notation, "1.70.0", Some(109417)),
|
||||
/// Allows `extern "rust-cold"`.
|
||||
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
|
||||
/// Shortern the tail expression lifetime
|
||||
(unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)),
|
||||
/// Allows the use of SIMD types in functions declared in `extern` blocks.
|
||||
(unstable, simd_ffi, "1.0.0", Some(27731)),
|
||||
/// Allows specialization of implementations (RFC 1210).
|
||||
|
@ -6,7 +6,6 @@
|
||||
//!
|
||||
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
|
||||
|
||||
use rustc_ast::visit::visit_opt;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
@ -168,7 +167,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||
hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
|
||||
}
|
||||
}
|
||||
visit_opt!(visitor, visit_expr, &blk.expr);
|
||||
if let Some(tail_expr) = blk.expr {
|
||||
if visitor.tcx.features().shorter_tail_lifetimes
|
||||
&& blk.span.edition().at_least_rust_2024()
|
||||
{
|
||||
visitor.terminating_scopes.insert(tail_expr.hir_id.local_id);
|
||||
}
|
||||
visitor.visit_expr(tail_expr);
|
||||
}
|
||||
}
|
||||
|
||||
visitor.cx = prev_cx;
|
||||
|
@ -1678,6 +1678,7 @@
|
||||
shadow_call_stack,
|
||||
shl,
|
||||
shl_assign,
|
||||
shorter_tail_lifetimes,
|
||||
should_panic,
|
||||
shr,
|
||||
shr_assign,
|
||||
|
8
tests/ui/drop/auxiliary/edition-2021-macros.rs
Normal file
8
tests/ui/drop/auxiliary/edition-2021-macros.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//@ edition:2021
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! edition_2021_block {
|
||||
($($c:tt)*) => {
|
||||
{ $($c)* }
|
||||
}
|
||||
}
|
9
tests/ui/drop/auxiliary/edition-2024-macros.rs
Normal file
9
tests/ui/drop/auxiliary/edition-2024-macros.rs
Normal file
@ -0,0 +1,9 @@
|
||||
//@ edition:2024
|
||||
//@ compile-flags: -Zunstable-options
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! edition_2024_block {
|
||||
($($c:tt)*) => {
|
||||
{ $($c)* }
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
error[E0716]: temporary value dropped while borrowed
|
||||
--> $DIR/tail-expr-drop-order-negative.rs:11:15
|
||||
|
|
||||
LL | x.replace(std::cell::RefCell::new(123).borrow()).is_some()
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - temporary value is freed at the end of this statement
|
||||
| |
|
||||
| creates a temporary value which is freed while still in use
|
||||
LL |
|
||||
LL | }
|
||||
| - borrow might be used here, when `x` is dropped and runs the destructor for type `Option<Ref<'_, i32>>`
|
||||
|
|
||||
= note: consider using a `let` binding to create a longer lived value
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0716`.
|
17
tests/ui/drop/tail-expr-drop-order-negative.rs
Normal file
17
tests/ui/drop/tail-expr-drop-order-negative.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ [edition2021] check-pass
|
||||
|
||||
#![feature(shorter_tail_lifetimes)]
|
||||
|
||||
fn why_would_you_do_this() -> bool {
|
||||
let mut x = None;
|
||||
// Make a temporary `RefCell` and put a `Ref` that borrows it in `x`.
|
||||
x.replace(std::cell::RefCell::new(123).borrow()).is_some()
|
||||
//[edition2024]~^ ERROR: temporary value dropped while borrowed
|
||||
}
|
||||
|
||||
fn main() {
|
||||
why_would_you_do_this();
|
||||
}
|
108
tests/ui/drop/tail-expr-drop-order.rs
Normal file
108
tests/ui/drop/tail-expr-drop-order.rs
Normal file
@ -0,0 +1,108 @@
|
||||
//@ aux-build:edition-2021-macros.rs
|
||||
//@ aux-build:edition-2024-macros.rs
|
||||
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||
//@ edition: 2024
|
||||
//@ run-pass
|
||||
|
||||
#![feature(shorter_tail_lifetimes)]
|
||||
#![allow(unused_imports)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_variables)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate edition_2021_macros;
|
||||
#[macro_use]
|
||||
extern crate edition_2024_macros;
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
|
||||
#[derive(Default)]
|
||||
struct DropOrderCollector(RefCell<Vec<u32>>);
|
||||
|
||||
struct LoudDrop<'a>(&'a DropOrderCollector, u32);
|
||||
|
||||
impl Drop for LoudDrop<'_> {
|
||||
fn drop(&mut self) {
|
||||
println!("{}", self.1);
|
||||
self.0.0.borrow_mut().push(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
impl DropOrderCollector {
|
||||
fn option_loud_drop(&self, n: u32) -> Option<LoudDrop> {
|
||||
Some(LoudDrop(self, n))
|
||||
}
|
||||
|
||||
fn loud_drop(&self, n: u32) -> LoudDrop {
|
||||
LoudDrop(self, n)
|
||||
}
|
||||
|
||||
fn assert_sorted(&self, expected: usize) {
|
||||
let result = self.0.borrow();
|
||||
assert_eq!(result.len(), expected);
|
||||
for i in 1..result.len() {
|
||||
assert!(
|
||||
result[i - 1] < result[i],
|
||||
"inversion at {} ({} followed by {})",
|
||||
i - 1,
|
||||
result[i - 1],
|
||||
result[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn edition_2021_around_2021() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2021_block! {
|
||||
let a = c.loud_drop(1);
|
||||
edition_2021_block! {
|
||||
let b = c.loud_drop(0);
|
||||
c.loud_drop(2).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn edition_2021_around_2024() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2021_block! {
|
||||
let a = c.loud_drop(2);
|
||||
edition_2024_block! {
|
||||
let b = c.loud_drop(1);
|
||||
c.loud_drop(0).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn edition_2024_around_2021() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2024_block! {
|
||||
let a = c.loud_drop(2);
|
||||
edition_2021_block! {
|
||||
let b = c.loud_drop(0);
|
||||
c.loud_drop(1).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn edition_2024_around_2024() {
|
||||
let c = DropOrderCollector::default();
|
||||
let _ = edition_2024_block! {
|
||||
let a = c.loud_drop(2);
|
||||
edition_2024_block! {
|
||||
let b = c.loud_drop(1);
|
||||
c.loud_drop(0).1
|
||||
}
|
||||
};
|
||||
c.assert_sorted(3);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
edition_2021_around_2021();
|
||||
edition_2021_around_2024();
|
||||
edition_2024_around_2021();
|
||||
edition_2024_around_2024();
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
fn f() -> usize {
|
||||
let c = std::cell::RefCell::new("..");
|
||||
c.borrow().len() //~ ERROR: `c` does not live long enough
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = f();
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
error[E0597]: `c` does not live long enough
|
||||
--> $DIR/feature-gate-shorter_tail_lifetimes.rs:3:5
|
||||
|
|
||||
LL | let c = std::cell::RefCell::new("..");
|
||||
| - binding `c` declared here
|
||||
LL | c.borrow().len()
|
||||
| ^---------
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
| a temporary with access to the borrow is created here ...
|
||||
LL | }
|
||||
| -
|
||||
| |
|
||||
| `c` dropped here while still borrowed
|
||||
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
|
||||
|
|
||||
= note: the temporary is part of an expression at the end of a block;
|
||||
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
|
||||
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
||||
|
|
||||
LL | let x = c.borrow().len(); x
|
||||
| +++++++ +++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
26
tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr
Normal file
26
tests/ui/lifetimes/refcell-in-tail-expr.edition2021.stderr
Normal file
@ -0,0 +1,26 @@
|
||||
error[E0597]: `cell` does not live long enough
|
||||
--> $DIR/refcell-in-tail-expr.rs:12:27
|
||||
|
|
||||
LL | let cell = std::cell::RefCell::new(0u8);
|
||||
| ---- binding `cell` declared here
|
||||
LL |
|
||||
LL | if let Ok(mut byte) = cell.try_borrow_mut() {
|
||||
| ^^^^-----------------
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
| a temporary with access to the borrow is created here ...
|
||||
...
|
||||
LL | }
|
||||
| -
|
||||
| |
|
||||
| `cell` dropped here while still borrowed
|
||||
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<RefMut<'_, u8>, BorrowMutError>`
|
||||
|
|
||||
help: consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped
|
||||
|
|
||||
LL | };
|
||||
| +
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
16
tests/ui/lifetimes/refcell-in-tail-expr.rs
Normal file
16
tests/ui/lifetimes/refcell-in-tail-expr.rs
Normal file
@ -0,0 +1,16 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ [edition2021] edition: 2021
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] check-pass
|
||||
|
||||
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
|
||||
|
||||
fn main() {
|
||||
let cell = std::cell::RefCell::new(0u8);
|
||||
|
||||
if let Ok(mut byte) = cell.try_borrow_mut() {
|
||||
//[edition2021]~^ ERROR: `cell` does not live long enough
|
||||
*byte = 1;
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
error[E0597]: `c` does not live long enough
|
||||
--> $DIR/shorter-tail-expr-lifetime.rs:10:5
|
||||
|
|
||||
LL | let c = std::cell::RefCell::new("..");
|
||||
| - binding `c` declared here
|
||||
LL | c.borrow().len()
|
||||
| ^---------
|
||||
| |
|
||||
| borrowed value does not live long enough
|
||||
| a temporary with access to the borrow is created here ...
|
||||
LL | }
|
||||
| -
|
||||
| |
|
||||
| `c` dropped here while still borrowed
|
||||
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
|
||||
|
|
||||
= note: the temporary is part of an expression at the end of a block;
|
||||
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
|
||||
help: for example, you could save the expression's value in a new local variable `x` and then make `x` be the expression at the end of the block
|
||||
|
|
||||
LL | let x = c.borrow().len(); x
|
||||
| +++++++ +++
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0597`.
|
15
tests/ui/lifetimes/shorter-tail-expr-lifetime.rs
Normal file
15
tests/ui/lifetimes/shorter-tail-expr-lifetime.rs
Normal file
@ -0,0 +1,15 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ [edition2024] run-pass
|
||||
|
||||
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
|
||||
|
||||
fn f() -> usize {
|
||||
let c = std::cell::RefCell::new("..");
|
||||
c.borrow().len() //[edition2021]~ ERROR: `c` does not live long enough
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _ = f();
|
||||
}
|
9
tests/ui/lifetimes/tail-expr-in-nested-expr.rs
Normal file
9
tests/ui/lifetimes/tail-expr-in-nested-expr.rs
Normal file
@ -0,0 +1,9 @@
|
||||
//@ edition: 2024
|
||||
//@ compile-flags: -Zunstable-options
|
||||
|
||||
#![feature(shorter_tail_lifetimes)]
|
||||
|
||||
fn main() {
|
||||
let _ = { String::new().as_str() }.len();
|
||||
//~^ ERROR temporary value dropped while borrowed
|
||||
}
|
15
tests/ui/lifetimes/tail-expr-in-nested-expr.stderr
Normal file
15
tests/ui/lifetimes/tail-expr-in-nested-expr.stderr
Normal file
@ -0,0 +1,15 @@
|
||||
error[E0716]: temporary value dropped while borrowed
|
||||
--> $DIR/tail-expr-in-nested-expr.rs:7:15
|
||||
|
|
||||
LL | let _ = { String::new().as_str() }.len();
|
||||
| ^^^^^^^^^^^^^---------
|
||||
| | |
|
||||
| | temporary value is freed at the end of this statement
|
||||
| creates a temporary value which is freed while still in use
|
||||
| borrow later used here
|
||||
|
|
||||
= note: consider using a `let` binding to create a longer lived value
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0716`.
|
29
tests/ui/lifetimes/tail-expr-lock-poisoning.rs
Normal file
29
tests/ui/lifetimes/tail-expr-lock-poisoning.rs
Normal file
@ -0,0 +1,29 @@
|
||||
//@ revisions: edition2021 edition2024
|
||||
//@ ignore-wasm no panic or subprocess support
|
||||
//@ [edition2024] compile-flags: -Zunstable-options
|
||||
//@ [edition2024] edition: 2024
|
||||
//@ run-pass
|
||||
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
struct PanicOnDrop;
|
||||
impl Drop for PanicOnDrop {
|
||||
fn drop(&mut self) {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
fn f(m: &Mutex<i32>) -> i32 {
|
||||
let _x = PanicOnDrop;
|
||||
*m.lock().unwrap()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let m = Mutex::new(0);
|
||||
let _ = std::panic::catch_unwind(|| f(&m));
|
||||
#[cfg(edition2024)]
|
||||
assert!(m.lock().is_ok());
|
||||
#[cfg(edition2021)]
|
||||
assert!(m.lock().is_err());
|
||||
}
|
@ -17,14 +17,14 @@ fn foo(x: &u32) -> &u32 {
|
||||
fn baz(x: &u32) -> &&u32 {
|
||||
let x = 22;
|
||||
&&x
|
||||
//~^ ERROR cannot return value referencing local variable
|
||||
//~^ ERROR cannot return value referencing local variable `x`
|
||||
//~| ERROR cannot return reference to temporary value
|
||||
}
|
||||
|
||||
fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 {
|
||||
let x = 22;
|
||||
&x
|
||||
//~^ ERROR cannot return reference to local variable
|
||||
//~^ ERROR cannot return reference to local variable `x`
|
||||
}
|
||||
|
||||
fn foobar<'a>(x: &'a u32) -> &'a u32 {
|
||||
|
Loading…
Reference in New Issue
Block a user