Skip to content

Commit cefd67d

Browse files
rescope temp lifetime in let-chain into IfElse
apply rules by span edition
1 parent eb33b43 commit cefd67d

30 files changed

+1102
-35
lines changed

Diff for: compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs

+21-8
Original file line numberDiff line numberDiff line change
@@ -1999,19 +1999,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
19991999
) {
20002000
let used_in_call = matches!(
20012001
explanation,
2002-
BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
2002+
BorrowExplanation::UsedLater(
2003+
_,
2004+
LaterUseKind::Call | LaterUseKind::Other,
2005+
_call_span,
2006+
_
2007+
)
20032008
);
20042009
if !used_in_call {
20052010
debug!("not later used in call");
20062011
return;
20072012
}
2013+
if matches!(
2014+
self.body.local_decls[issued_borrow.borrowed_place.local].local_info(),
2015+
LocalInfo::IfThenRescopeTemp { .. }
2016+
) {
2017+
// A better suggestion will be issued by the `if_let_rescope` lint
2018+
return;
2019+
}
20082020

2009-
let use_span =
2010-
if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
2011-
Some(use_span)
2012-
} else {
2013-
None
2014-
};
2021+
let use_span = if let BorrowExplanation::UsedLater(_, LaterUseKind::Other, use_span, _) =
2022+
explanation
2023+
{
2024+
Some(use_span)
2025+
} else {
2026+
None
2027+
};
20152028

20162029
let outer_call_loc =
20172030
if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
@@ -2862,7 +2875,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
28622875
// and `move` will not help here.
28632876
(
28642877
Some(name),
2865-
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
2878+
BorrowExplanation::UsedLater(_, LaterUseKind::ClosureCapture, var_or_use_span, _),
28662879
) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
28672880
.report_escaping_closure_capture(
28682881
borrow_spans,

Diff for: compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs

+86-9
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use crate::{MirBorrowckCtxt, WriteKind};
3030

3131
#[derive(Debug)]
3232
pub(crate) enum BorrowExplanation<'tcx> {
33-
UsedLater(LaterUseKind, Span, Option<Span>),
33+
UsedLater(Local, LaterUseKind, Span, Option<Span>),
3434
UsedLaterInLoop(LaterUseKind, Span, Option<Span>),
3535
UsedLaterWhenDropped {
3636
drop_loc: Location,
@@ -99,17 +99,39 @@ impl<'tcx> BorrowExplanation<'tcx> {
9999
}
100100
}
101101
match *self {
102-
BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => {
102+
BorrowExplanation::UsedLater(
103+
dropped_local,
104+
later_use_kind,
105+
var_or_use_span,
106+
path_span,
107+
) => {
103108
let message = match later_use_kind {
104109
LaterUseKind::TraitCapture => "captured here by trait object",
105110
LaterUseKind::ClosureCapture => "captured here by closure",
106111
LaterUseKind::Call => "used by call",
107112
LaterUseKind::FakeLetRead => "stored here",
108113
LaterUseKind::Other => "used here",
109114
};
110-
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
111-
if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
112-
if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
115+
let local_decl = &body.local_decls[dropped_local];
116+
117+
if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
118+
&& let Some((_, hir::Node::Expr(expr))) = tcx.hir().parent_iter(if_then).next()
119+
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
120+
&& let hir::ExprKind::Let(&hir::LetExpr {
121+
span: _,
122+
pat,
123+
init,
124+
// FIXME(#101728): enable rewrite when type ascription is stabilized again
125+
ty: None,
126+
recovered: _,
127+
}) = cond.kind
128+
&& pat.span.can_be_used_for_suggestions()
129+
&& let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
130+
{
131+
suggest_rewrite_if_let(expr, &pat, init, conseq, alt, err);
132+
} else if path_span.map_or(true, |path_span| path_span == var_or_use_span) {
133+
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
134+
if borrow_span.map_or(true, |sp| !sp.overlaps(var_or_use_span)) {
113135
err.span_label(
114136
var_or_use_span,
115137
format!("{borrow_desc}borrow later {message}"),
@@ -255,6 +277,22 @@ impl<'tcx> BorrowExplanation<'tcx> {
255277
Applicability::MaybeIncorrect,
256278
);
257279
};
280+
} else if let &LocalInfo::IfThenRescopeTemp { if_then } =
281+
local_decl.local_info()
282+
&& let hir::Node::Expr(expr) = tcx.hir_node(if_then)
283+
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
284+
&& let hir::ExprKind::Let(&hir::LetExpr {
285+
span: _,
286+
pat,
287+
init,
288+
// FIXME(#101728): enable rewrite when type ascription is stabilized again
289+
ty: None,
290+
recovered: _,
291+
}) = cond.kind
292+
&& pat.span.can_be_used_for_suggestions()
293+
&& let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
294+
{
295+
suggest_rewrite_if_let(expr, &pat, init, conseq, alt, err);
258296
}
259297
}
260298
}
@@ -390,6 +428,38 @@ impl<'tcx> BorrowExplanation<'tcx> {
390428
}
391429
}
392430

431+
fn suggest_rewrite_if_let<'tcx>(
432+
expr: &hir::Expr<'tcx>,
433+
pat: &str,
434+
init: &hir::Expr<'tcx>,
435+
conseq: &hir::Expr<'tcx>,
436+
alt: Option<&hir::Expr<'tcx>>,
437+
err: &mut Diag<'_>,
438+
) {
439+
err.span_note(
440+
conseq.span.shrink_to_hi(),
441+
"lifetime for temporaries generated in `if let`s have been shorted in Edition 2024",
442+
);
443+
if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
444+
let mut sugg = vec![
445+
(expr.span.shrink_to_lo().between(init.span), "match ".into()),
446+
(conseq.span.shrink_to_lo(), format!(" {{ {pat} => ")),
447+
];
448+
let expr_end = expr.span.shrink_to_hi();
449+
if let Some(alt) = alt {
450+
sugg.push((conseq.span.between(alt.span), format!(" _ => ")));
451+
sugg.push((expr_end, "}".into()));
452+
} else {
453+
sugg.push((expr_end, " _ => {} }".into()));
454+
}
455+
err.multipart_suggestion(
456+
"consider rewriting the `if` into `match` which preserves the extended lifetime",
457+
sugg,
458+
Applicability::MachineApplicable,
459+
);
460+
}
461+
}
462+
393463
impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
394464
fn free_region_constraint_info(
395465
&self,
@@ -465,14 +535,21 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
465535
.or_else(|| self.borrow_spans(span, location));
466536

467537
if use_in_later_iteration_of_loop {
468-
let later_use = self.later_use_kind(borrow, spans, use_location);
469-
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)
538+
let (later_use_kind, var_or_use_span, path_span) =
539+
self.later_use_kind(borrow, spans, use_location);
540+
BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span)
470541
} else {
471542
// Check if the location represents a `FakeRead`, and adapt the error
472543
// message to the `FakeReadCause` it is from: in particular,
473544
// the ones inserted in optimized `let var = <expr>` patterns.
474-
let later_use = self.later_use_kind(borrow, spans, location);
475-
BorrowExplanation::UsedLater(later_use.0, later_use.1, later_use.2)
545+
let (later_use_kind, var_or_use_span, path_span) =
546+
self.later_use_kind(borrow, spans, location);
547+
BorrowExplanation::UsedLater(
548+
borrow.borrowed_place.local,
549+
later_use_kind,
550+
var_or_use_span,
551+
path_span,
552+
)
476553
}
477554
}
478555

Diff for: compiler/rustc_feature/src/unstable.rs

+2
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,8 @@ declare_features! (
497497
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
498498
/// Allows `if let` guard in match arms.
499499
(unstable, if_let_guard, "1.47.0", Some(51114)),
500+
/// Rescoping temporaries in `if let` to align with Rust 2024.
501+
(unstable, if_let_rescope, "CURRENT_RUSTC_VERSION", Some(124085)),
500502
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
501503
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
502504
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.

Diff for: compiler/rustc_hir_analysis/src/check/region.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -472,7 +472,12 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
472472

473473
hir::ExprKind::If(cond, then, Some(otherwise)) => {
474474
let expr_cx = visitor.cx;
475-
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
475+
let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope {
476+
ScopeData::IfThenRescope
477+
} else {
478+
ScopeData::IfThen
479+
};
480+
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
476481
visitor.cx.var_parent = visitor.cx.parent;
477482
visitor.visit_expr(cond);
478483
visitor.visit_expr(then);
@@ -482,7 +487,12 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
482487

483488
hir::ExprKind::If(cond, then, None) => {
484489
let expr_cx = visitor.cx;
485-
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
490+
let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope {
491+
ScopeData::IfThenRescope
492+
} else {
493+
ScopeData::IfThen
494+
};
495+
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
486496
visitor.cx.var_parent = visitor.cx.parent;
487497
visitor.visit_expr(cond);
488498
visitor.visit_expr(then);

Diff for: compiler/rustc_lint/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
333333
*[other] {" "}{$identifier_type}
334334
} Unicode general security profile
335335
336+
lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024
337+
.label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
338+
.help = the value is now dropped here in Edition 2024
339+
.suggestion = rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021
340+
336341
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
337342
338343
lint_ill_formed_attribute_input = {$num_suggestions ->

0 commit comments

Comments
 (0)