Skip to content

Commit 21d1190

Browse files
tail expression behind terminating scope
1 parent 6c90ac8 commit 21d1190

File tree

9 files changed

+123
-23
lines changed

9 files changed

+123
-23
lines changed

compiler/rustc_ast_lowering/src/expr.rs

+35-19
Original file line numberDiff line numberDiff line change
@@ -1716,24 +1716,28 @@ impl<'hir> LoweringContext<'_, 'hir> {
17161716
// `mut iter => { ... }`
17171717
let iter_arm = self.arm(iter_pat, loop_expr);
17181718

1719-
let into_iter_expr = match loop_kind {
1719+
let match_expr = match loop_kind {
17201720
ForLoopKind::For => {
17211721
// `::std::iter::IntoIterator::into_iter(<head>)`
1722-
self.expr_call_lang_item_fn(
1722+
let into_iter_expr = self.expr_call_lang_item_fn(
17231723
head_span,
17241724
hir::LangItem::IntoIterIntoIter,
17251725
arena_vec![self; head],
1726-
)
1726+
);
1727+
1728+
self.arena.alloc(self.expr_match(
1729+
for_span,
1730+
into_iter_expr,
1731+
arena_vec![self; iter_arm],
1732+
hir::MatchSource::ForLoopDesugar,
1733+
))
17271734
}
1728-
// ` unsafe { Pin::new_unchecked(&mut into_async_iter(<head>)) }`
1735+
// `match into_async_iter(<head>) { ref mut iter => match unsafe { Pin::new_unchecked(iter) } { ... } }`
17291736
ForLoopKind::ForAwait => {
1730-
// `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
1731-
let iter = self.expr_call_lang_item_fn(
1732-
head_span,
1733-
hir::LangItem::IntoAsyncIterIntoIter,
1734-
arena_vec![self; head],
1735-
);
1736-
let iter = self.expr_mut_addr_of(head_span, iter);
1737+
let iter_ident = iter;
1738+
let (async_iter_pat, async_iter_pat_id) =
1739+
self.pat_ident_binding_mode(head_span, iter_ident, hir::BindingMode::REF_MUT);
1740+
let iter = self.expr_ident_mut(head_span, iter_ident, async_iter_pat_id);
17371741
// `Pin::new_unchecked(...)`
17381742
let iter = self.arena.alloc(self.expr_call_lang_item_fn_mut(
17391743
head_span,
@@ -1742,17 +1746,29 @@ impl<'hir> LoweringContext<'_, 'hir> {
17421746
));
17431747
// `unsafe { ... }`
17441748
let iter = self.arena.alloc(self.expr_unsafe(iter));
1745-
iter
1749+
let inner_match_expr = self.arena.alloc(self.expr_match(
1750+
for_span,
1751+
iter,
1752+
arena_vec![self; iter_arm],
1753+
hir::MatchSource::ForLoopDesugar,
1754+
));
1755+
1756+
// `::core::async_iter::IntoAsyncIterator::into_async_iter(<head>)`
1757+
let iter = self.expr_call_lang_item_fn(
1758+
head_span,
1759+
hir::LangItem::IntoAsyncIterIntoIter,
1760+
arena_vec![self; head],
1761+
);
1762+
let iter_arm = self.arm(async_iter_pat, inner_match_expr);
1763+
self.arena.alloc(self.expr_match(
1764+
for_span,
1765+
iter,
1766+
arena_vec![self; iter_arm],
1767+
hir::MatchSource::ForLoopDesugar,
1768+
))
17461769
}
17471770
};
17481771

1749-
let match_expr = self.arena.alloc(self.expr_match(
1750-
for_span,
1751-
into_iter_expr,
1752-
arena_vec![self; iter_arm],
1753-
hir::MatchSource::ForLoopDesugar,
1754-
));
1755-
17561772
// This is effectively `{ let _result = ...; _result }`.
17571773
// The construct was introduced in #21984 and is necessary to make sure that
17581774
// temporaries in the `head` expression are dropped and do not leak to the

compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,8 @@ declare_features! (
585585
(incomplete, return_type_notation, "1.70.0", Some(109417)),
586586
/// Allows `extern "rust-cold"`.
587587
(unstable, rust_cold_cc, "1.63.0", Some(97544)),
588+
/// Shortern the tail expression lifetime
589+
(unstable, shorter_tail_lifetimes, "1.79.0", Some(123739)),
588590
/// Allows the use of SIMD types in functions declared in `extern` blocks.
589591
(unstable, simd_ffi, "1.0.0", Some(27731)),
590592
/// Allows specialization of implementations (RFC 1210).

compiler/rustc_hir_analysis/src/check/region.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//!
77
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
88
9-
use rustc_ast::visit::walk_list;
109
use rustc_data_structures::fx::FxHashSet;
1110
use rustc_hir as hir;
1211
use rustc_hir::def_id::DefId;
@@ -167,7 +166,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
167166
hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
168167
}
169168
}
170-
walk_list!(visitor, visit_expr, &blk.expr);
169+
if let Some(tail_expr) = blk.expr {
170+
if visitor.tcx.features().shorter_tail_lifetimes
171+
&& tail_expr.span.edition().at_least_rust_2024()
172+
{
173+
visitor.terminating_scopes.insert(tail_expr.hir_id.local_id);
174+
}
175+
visitor.visit_expr(tail_expr);
176+
}
171177
}
172178

173179
visitor.cx = prev_cx;

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1660,6 +1660,7 @@ symbols! {
16601660
shadow_call_stack,
16611661
shl,
16621662
shl_assign,
1663+
shorter_tail_lifetimes,
16631664
should_panic,
16641665
shr,
16651666
shr_assign,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn f() -> usize {
2+
let c = std::cell::RefCell::new("..");
3+
c.borrow().len() //~ ERROR: `c` does not live long enough
4+
}
5+
6+
fn main() {
7+
let _ = f();
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0597]: `c` does not live long enough
2+
--> $DIR/feature-gate-shorter_tail_lifetimes.rs:3:5
3+
|
4+
LL | let c = std::cell::RefCell::new("..");
5+
| - binding `c` declared here
6+
LL | c.borrow().len()
7+
| ^---------
8+
| |
9+
| borrowed value does not live long enough
10+
| a temporary with access to the borrow is created here ...
11+
LL | }
12+
| -
13+
| |
14+
| `c` dropped here while still borrowed
15+
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
16+
|
17+
= note: the temporary is part of an expression at the end of a block;
18+
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
19+
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
20+
|
21+
LL | let x = c.borrow().len(); x
22+
| +++++++ +++
23+
24+
error: aborting due to 1 previous error
25+
26+
For more information about this error, try `rustc --explain E0597`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0597]: `c` does not live long enough
2+
--> $DIR/shorter-tail-expr-lifetime.rs:10:5
3+
|
4+
LL | let c = std::cell::RefCell::new("..");
5+
| - binding `c` declared here
6+
LL | c.borrow().len()
7+
| ^---------
8+
| |
9+
| borrowed value does not live long enough
10+
| a temporary with access to the borrow is created here ...
11+
LL | }
12+
| -
13+
| |
14+
| `c` dropped here while still borrowed
15+
| ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Ref<'_, &str>`
16+
|
17+
= note: the temporary is part of an expression at the end of a block;
18+
consider forcing this temporary to be dropped sooner, before the block's local variables are dropped
19+
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
20+
|
21+
LL | let x = c.borrow().len(); x
22+
| +++++++ +++
23+
24+
error: aborting due to 1 previous error
25+
26+
For more information about this error, try `rustc --explain E0597`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ revisions: edition2021 edition2024
2+
//@ [edition2024] compile-flags: -Zunstable-options
3+
//@ [edition2024] edition: 2024
4+
//@ [edition2024] run-pass
5+
6+
#![cfg_attr(edition2024, feature(shorter_tail_lifetimes))]
7+
8+
fn f() -> usize {
9+
let c = std::cell::RefCell::new("..");
10+
c.borrow().len() //[edition2021]~ ERROR: `c` does not live long enough
11+
}
12+
13+
fn main() {
14+
let _ = f();
15+
}

tests/ui/nll/issue-52534-1.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ fn foo(x: &u32) -> &u32 {
1717
fn baz(x: &u32) -> &&u32 {
1818
let x = 22;
1919
&&x
20-
//~^ ERROR cannot return value referencing local variable
20+
//~^ ERROR cannot return value referencing local variable `x`
2121
//~| ERROR cannot return reference to temporary value
2222
}
2323

2424
fn foobazbar<'a>(x: u32, y: &'a u32) -> &'a u32 {
2525
let x = 22;
2626
&x
27-
//~^ ERROR cannot return reference to local variable
27+
//~^ ERROR cannot return reference to local variable `x`
2828
}
2929

3030
fn foobar<'a>(x: &'a u32) -> &'a u32 {

0 commit comments

Comments
 (0)