mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
rescope temp lifetime in let-chain into IfElse
apply rules by span edition
This commit is contained in:
parent
33855f80d4
commit
f93df1f7dc
@ -1999,19 +1999,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||||||
) {
|
) {
|
||||||
let used_in_call = matches!(
|
let used_in_call = matches!(
|
||||||
explanation,
|
explanation,
|
||||||
BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
|
BorrowExplanation::UsedLater(
|
||||||
|
_,
|
||||||
|
LaterUseKind::Call | LaterUseKind::Other,
|
||||||
|
_call_span,
|
||||||
|
_
|
||||||
|
)
|
||||||
);
|
);
|
||||||
if !used_in_call {
|
if !used_in_call {
|
||||||
debug!("not later used in call");
|
debug!("not later used in call");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if matches!(
|
||||||
|
self.body.local_decls[issued_borrow.borrowed_place.local].local_info(),
|
||||||
|
LocalInfo::IfThenRescopeTemp { .. }
|
||||||
|
) {
|
||||||
|
// A better suggestion will be issued by the `if_let_rescope` lint
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let use_span =
|
let use_span = if let BorrowExplanation::UsedLater(_, LaterUseKind::Other, use_span, _) =
|
||||||
if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
|
explanation
|
||||||
Some(use_span)
|
{
|
||||||
} else {
|
Some(use_span)
|
||||||
None
|
} else {
|
||||||
};
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let outer_call_loc =
|
let outer_call_loc =
|
||||||
if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
|
if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
|
||||||
@ -2862,7 +2875,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||||||
// and `move` will not help here.
|
// and `move` will not help here.
|
||||||
(
|
(
|
||||||
Some(name),
|
Some(name),
|
||||||
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
|
BorrowExplanation::UsedLater(_, LaterUseKind::ClosureCapture, var_or_use_span, _),
|
||||||
) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
|
) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
|
||||||
.report_escaping_closure_capture(
|
.report_escaping_closure_capture(
|
||||||
borrow_spans,
|
borrow_spans,
|
||||||
|
@ -30,7 +30,7 @@ use crate::{MirBorrowckCtxt, WriteKind};
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum BorrowExplanation<'tcx> {
|
pub(crate) enum BorrowExplanation<'tcx> {
|
||||||
UsedLater(LaterUseKind, Span, Option<Span>),
|
UsedLater(Local, LaterUseKind, Span, Option<Span>),
|
||||||
UsedLaterInLoop(LaterUseKind, Span, Option<Span>),
|
UsedLaterInLoop(LaterUseKind, Span, Option<Span>),
|
||||||
UsedLaterWhenDropped {
|
UsedLaterWhenDropped {
|
||||||
drop_loc: Location,
|
drop_loc: Location,
|
||||||
@ -99,7 +99,12 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
match *self {
|
match *self {
|
||||||
BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => {
|
BorrowExplanation::UsedLater(
|
||||||
|
dropped_local,
|
||||||
|
later_use_kind,
|
||||||
|
var_or_use_span,
|
||||||
|
path_span,
|
||||||
|
) => {
|
||||||
let message = match later_use_kind {
|
let message = match later_use_kind {
|
||||||
LaterUseKind::TraitCapture => "captured here by trait object",
|
LaterUseKind::TraitCapture => "captured here by trait object",
|
||||||
LaterUseKind::ClosureCapture => "captured here by closure",
|
LaterUseKind::ClosureCapture => "captured here by closure",
|
||||||
@ -107,9 +112,26 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
LaterUseKind::FakeLetRead => "stored here",
|
LaterUseKind::FakeLetRead => "stored here",
|
||||||
LaterUseKind::Other => "used here",
|
LaterUseKind::Other => "used here",
|
||||||
};
|
};
|
||||||
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
|
let local_decl = &body.local_decls[dropped_local];
|
||||||
if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
|
|
||||||
if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
|
if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
|
||||||
|
&& let Some((_, hir::Node::Expr(expr))) = tcx.hir().parent_iter(if_then).next()
|
||||||
|
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
|
||||||
|
&& let hir::ExprKind::Let(&hir::LetExpr {
|
||||||
|
span: _,
|
||||||
|
pat,
|
||||||
|
init,
|
||||||
|
// FIXME(#101728): enable rewrite when type ascription is stabilized again
|
||||||
|
ty: None,
|
||||||
|
recovered: _,
|
||||||
|
}) = cond.kind
|
||||||
|
&& pat.span.can_be_used_for_suggestions()
|
||||||
|
&& let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
|
||||||
|
{
|
||||||
|
suggest_rewrite_if_let(expr, &pat, init, conseq, alt, err);
|
||||||
|
} else if path_span.map_or(true, |path_span| path_span == var_or_use_span) {
|
||||||
|
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
|
||||||
|
if borrow_span.map_or(true, |sp| !sp.overlaps(var_or_use_span)) {
|
||||||
err.span_label(
|
err.span_label(
|
||||||
var_or_use_span,
|
var_or_use_span,
|
||||||
format!("{borrow_desc}borrow later {message}"),
|
format!("{borrow_desc}borrow later {message}"),
|
||||||
@ -255,6 +277,22 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
} else if let &LocalInfo::IfThenRescopeTemp { if_then } =
|
||||||
|
local_decl.local_info()
|
||||||
|
&& let hir::Node::Expr(expr) = tcx.hir_node(if_then)
|
||||||
|
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
|
||||||
|
&& let hir::ExprKind::Let(&hir::LetExpr {
|
||||||
|
span: _,
|
||||||
|
pat,
|
||||||
|
init,
|
||||||
|
// FIXME(#101728): enable rewrite when type ascription is stabilized again
|
||||||
|
ty: None,
|
||||||
|
recovered: _,
|
||||||
|
}) = cond.kind
|
||||||
|
&& pat.span.can_be_used_for_suggestions()
|
||||||
|
&& let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
|
||||||
|
{
|
||||||
|
suggest_rewrite_if_let(expr, &pat, init, conseq, alt, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -390,6 +428,38 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn suggest_rewrite_if_let<'tcx>(
|
||||||
|
expr: &hir::Expr<'tcx>,
|
||||||
|
pat: &str,
|
||||||
|
init: &hir::Expr<'tcx>,
|
||||||
|
conseq: &hir::Expr<'tcx>,
|
||||||
|
alt: Option<&hir::Expr<'tcx>>,
|
||||||
|
err: &mut Diag<'_>,
|
||||||
|
) {
|
||||||
|
err.span_note(
|
||||||
|
conseq.span.shrink_to_hi(),
|
||||||
|
"lifetime for temporaries generated in `if let`s have been shorted in Edition 2024",
|
||||||
|
);
|
||||||
|
if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
|
||||||
|
let mut sugg = vec![
|
||||||
|
(expr.span.shrink_to_lo().between(init.span), "match ".into()),
|
||||||
|
(conseq.span.shrink_to_lo(), format!(" {{ {pat} => ")),
|
||||||
|
];
|
||||||
|
let expr_end = expr.span.shrink_to_hi();
|
||||||
|
if let Some(alt) = alt {
|
||||||
|
sugg.push((conseq.span.between(alt.span), format!(" _ => ")));
|
||||||
|
sugg.push((expr_end, "}".into()));
|
||||||
|
} else {
|
||||||
|
sugg.push((expr_end, " _ => {} }".into()));
|
||||||
|
}
|
||||||
|
err.multipart_suggestion(
|
||||||
|
"consider rewriting the `if` into `match` which preserves the extended lifetime",
|
||||||
|
sugg,
|
||||||
|
Applicability::MachineApplicable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||||
fn free_region_constraint_info(
|
fn free_region_constraint_info(
|
||||||
&self,
|
&self,
|
||||||
@ -465,14 +535,21 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||||||
.or_else(|| self.borrow_spans(span, location));
|
.or_else(|| self.borrow_spans(span, location));
|
||||||
|
|
||||||
if use_in_later_iteration_of_loop {
|
if use_in_later_iteration_of_loop {
|
||||||
let later_use = self.later_use_kind(borrow, spans, use_location);
|
let (later_use_kind, var_or_use_span, path_span) =
|
||||||
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)
|
self.later_use_kind(borrow, spans, use_location);
|
||||||
|
BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span)
|
||||||
} else {
|
} else {
|
||||||
// Check if the location represents a `FakeRead`, and adapt the error
|
// Check if the location represents a `FakeRead`, and adapt the error
|
||||||
// message to the `FakeReadCause` it is from: in particular,
|
// message to the `FakeReadCause` it is from: in particular,
|
||||||
// the ones inserted in optimized `let var = <expr>` patterns.
|
// the ones inserted in optimized `let var = <expr>` patterns.
|
||||||
let later_use = self.later_use_kind(borrow, spans, location);
|
let (later_use_kind, var_or_use_span, path_span) =
|
||||||
BorrowExplanation::UsedLater(later_use.0, later_use.1, later_use.2)
|
self.later_use_kind(borrow, spans, location);
|
||||||
|
BorrowExplanation::UsedLater(
|
||||||
|
borrow.borrowed_place.local,
|
||||||
|
later_use_kind,
|
||||||
|
var_or_use_span,
|
||||||
|
path_span,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -497,6 +497,8 @@ declare_features! (
|
|||||||
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
|
(unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)),
|
||||||
/// Allows `if let` guard in match arms.
|
/// Allows `if let` guard in match arms.
|
||||||
(unstable, if_let_guard, "1.47.0", Some(51114)),
|
(unstable, if_let_guard, "1.47.0", Some(51114)),
|
||||||
|
/// Rescoping temporaries in `if let` to align with Rust 2024.
|
||||||
|
(unstable, if_let_rescope, "CURRENT_RUSTC_VERSION", Some(124085)),
|
||||||
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
|
/// Allows `impl Trait` to be used inside associated types (RFC 2515).
|
||||||
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
|
(unstable, impl_trait_in_assoc_type, "1.70.0", Some(63063)),
|
||||||
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
|
/// Allows `impl Trait` as output type in `Fn` traits in return position of functions.
|
||||||
|
@ -472,7 +472,12 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
|||||||
|
|
||||||
hir::ExprKind::If(cond, then, Some(otherwise)) => {
|
hir::ExprKind::If(cond, then, Some(otherwise)) => {
|
||||||
let expr_cx = visitor.cx;
|
let expr_cx = visitor.cx;
|
||||||
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
|
let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope {
|
||||||
|
ScopeData::IfThenRescope
|
||||||
|
} else {
|
||||||
|
ScopeData::IfThen
|
||||||
|
};
|
||||||
|
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
visitor.visit_expr(cond);
|
visitor.visit_expr(cond);
|
||||||
visitor.visit_expr(then);
|
visitor.visit_expr(then);
|
||||||
@ -482,7 +487,12 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
|||||||
|
|
||||||
hir::ExprKind::If(cond, then, None) => {
|
hir::ExprKind::If(cond, then, None) => {
|
||||||
let expr_cx = visitor.cx;
|
let expr_cx = visitor.cx;
|
||||||
visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen });
|
let data = if expr.span.at_least_rust_2024() && visitor.tcx.features().if_let_rescope {
|
||||||
|
ScopeData::IfThenRescope
|
||||||
|
} else {
|
||||||
|
ScopeData::IfThen
|
||||||
|
};
|
||||||
|
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
visitor.visit_expr(cond);
|
visitor.visit_expr(cond);
|
||||||
visitor.visit_expr(then);
|
visitor.visit_expr(then);
|
||||||
|
@ -334,6 +334,11 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len ->
|
|||||||
*[other] {" "}{$identifier_type}
|
*[other] {" "}{$identifier_type}
|
||||||
} Unicode general security profile
|
} Unicode general security profile
|
||||||
|
|
||||||
|
lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
.label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
||||||
|
.help = the value is now dropped here in Edition 2024
|
||||||
|
.suggestion = rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021
|
||||||
|
|
||||||
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
|
lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level
|
||||||
|
|
||||||
lint_ill_formed_attribute_input = {$num_suggestions ->
|
lint_ill_formed_attribute_input = {$num_suggestions ->
|
||||||
|
312
compiler/rustc_lint/src/if_let_rescope.rs
Normal file
312
compiler/rustc_lint/src/if_let_rescope.rs
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
|
use hir::intravisit::Visitor;
|
||||||
|
use rustc_ast::Recovered;
|
||||||
|
use rustc_hir as hir;
|
||||||
|
use rustc_macros::{LintDiagnostic, Subdiagnostic};
|
||||||
|
use rustc_session::lint::FutureIncompatibilityReason;
|
||||||
|
use rustc_session::{declare_lint, declare_lint_pass};
|
||||||
|
use rustc_span::edition::Edition;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
use crate::{LateContext, LateLintPass};
|
||||||
|
|
||||||
|
declare_lint! {
|
||||||
|
/// The `if_let_rescope` lint detects cases where a temporary value with
|
||||||
|
/// significant drop is generated on the right hand side of `if let`
|
||||||
|
/// and suggests a rewrite into `match` when possible.
|
||||||
|
///
|
||||||
|
/// ### Example
|
||||||
|
///
|
||||||
|
/// ```rust,edition2021
|
||||||
|
/// #![feature(if_let_rescope)]
|
||||||
|
/// #![warn(if_let_rescope)]
|
||||||
|
/// #![allow(unused_variables)]
|
||||||
|
///
|
||||||
|
/// struct Droppy;
|
||||||
|
/// impl Drop for Droppy {
|
||||||
|
/// fn drop(&mut self) {
|
||||||
|
/// // Custom destructor, including this `drop` implementation, is considered
|
||||||
|
/// // significant.
|
||||||
|
/// // Rust does not check whether this destructor emits side-effects that can
|
||||||
|
/// // lead to observable change in program semantics, when the drop order changes.
|
||||||
|
/// // Rust biases to be on the safe side, so that you can apply discretion whether
|
||||||
|
/// // this change indeed breaches any contract or specification that your code needs
|
||||||
|
/// // to honour.
|
||||||
|
/// println!("dropped");
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// impl Droppy {
|
||||||
|
/// fn get(&self) -> Option<u8> {
|
||||||
|
/// None
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn main() {
|
||||||
|
/// if let Some(value) = Droppy.get() {
|
||||||
|
/// // do something
|
||||||
|
/// } else {
|
||||||
|
/// // do something else
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// {{produces}}
|
||||||
|
///
|
||||||
|
/// ### Explanation
|
||||||
|
///
|
||||||
|
/// With Edition 2024, temporaries generated while evaluating `if let`s
|
||||||
|
/// will be dropped before the `else` block.
|
||||||
|
/// This lint captures a possible change in runtime behaviour due to
|
||||||
|
/// a change in sequence of calls to significant `Drop::drop` destructors.
|
||||||
|
///
|
||||||
|
/// A significant [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html)
|
||||||
|
/// destructor here refers to an explicit, arbitrary implementation of the `Drop` trait on the type
|
||||||
|
/// with exceptions including `Vec`, `Box`, `Rc`, `BTreeMap` and `HashMap`
|
||||||
|
/// that are marked by the compiler otherwise so long that the generic types have
|
||||||
|
/// no significant destructor recursively.
|
||||||
|
/// In other words, a type has a significant drop destructor when it has a `Drop` implementation
|
||||||
|
/// or its destructor invokes a significant destructor on a type.
|
||||||
|
/// Since we cannot completely reason about the change by just inspecting the existence of
|
||||||
|
/// a significant destructor, this lint remains only a suggestion and is set to `allow` by default.
|
||||||
|
///
|
||||||
|
/// Whenever possible, a rewrite into an equivalent `match` expression that
|
||||||
|
/// observe the same order of calls to such destructors is proposed by this lint.
|
||||||
|
/// Authors may take their own discretion whether the rewrite suggestion shall be
|
||||||
|
/// accepted, or rejected to continue the use of the `if let` expression.
|
||||||
|
pub IF_LET_RESCOPE,
|
||||||
|
Allow,
|
||||||
|
"`if let` assigns a shorter lifetime to temporary values being pattern-matched against in Edition 2024 and \
|
||||||
|
rewriting in `match` is an option to preserve the semantics up to Edition 2021",
|
||||||
|
@future_incompatible = FutureIncompatibleInfo {
|
||||||
|
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||||
|
reference: "issue #124085 <https://github.com/rust-lang/rust/issues/124085>",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_lint_pass!(
|
||||||
|
/// Lint for potential change in program semantics of `if let`s
|
||||||
|
IfLetRescope => [IF_LET_RESCOPE]
|
||||||
|
);
|
||||||
|
|
||||||
|
impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
|
||||||
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||||
|
if !expr.span.edition().at_least_rust_2021() || !cx.tcx.features().if_let_rescope {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let hir::ExprKind::If(cond, conseq, alt) = expr.kind else { return };
|
||||||
|
let hir::ExprKind::Let(&hir::LetExpr {
|
||||||
|
span,
|
||||||
|
pat,
|
||||||
|
init,
|
||||||
|
ty: ty_ascription,
|
||||||
|
recovered: Recovered::No,
|
||||||
|
}) = cond.kind
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let source_map = cx.tcx.sess.source_map();
|
||||||
|
let expr_end = expr.span.shrink_to_hi();
|
||||||
|
let if_let_pat = expr.span.shrink_to_lo().between(init.span);
|
||||||
|
let before_conseq = conseq.span.shrink_to_lo();
|
||||||
|
let lifetime_end = source_map.end_point(conseq.span);
|
||||||
|
|
||||||
|
if let ControlFlow::Break(significant_dropper) =
|
||||||
|
(FindSignificantDropper { cx }).visit_expr(init)
|
||||||
|
{
|
||||||
|
let lint_without_suggestion = || {
|
||||||
|
cx.tcx.emit_node_span_lint(
|
||||||
|
IF_LET_RESCOPE,
|
||||||
|
expr.hir_id,
|
||||||
|
span,
|
||||||
|
IfLetRescopeRewrite { significant_dropper, lifetime_end, sugg: None },
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if ty_ascription.is_some()
|
||||||
|
|| !expr.span.can_be_used_for_suggestions()
|
||||||
|
|| !pat.span.can_be_used_for_suggestions()
|
||||||
|
{
|
||||||
|
// Our `match` rewrites does not support type ascription,
|
||||||
|
// so we just bail.
|
||||||
|
// Alternatively when the span comes from proc macro expansion,
|
||||||
|
// we will also bail.
|
||||||
|
// FIXME(#101728): change this when type ascription syntax is stabilized again
|
||||||
|
lint_without_suggestion();
|
||||||
|
} else {
|
||||||
|
let Ok(pat) = source_map.span_to_snippet(pat.span) else {
|
||||||
|
lint_without_suggestion();
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if let Some(alt) = alt {
|
||||||
|
let alt_start = conseq.span.between(alt.span);
|
||||||
|
if !alt_start.can_be_used_for_suggestions() {
|
||||||
|
lint_without_suggestion();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cx.tcx.emit_node_span_lint(
|
||||||
|
IF_LET_RESCOPE,
|
||||||
|
expr.hir_id,
|
||||||
|
span,
|
||||||
|
IfLetRescopeRewrite {
|
||||||
|
significant_dropper,
|
||||||
|
lifetime_end,
|
||||||
|
sugg: Some(IfLetRescopeRewriteSuggestion::WithElse {
|
||||||
|
if_let_pat,
|
||||||
|
before_conseq,
|
||||||
|
pat,
|
||||||
|
expr_end,
|
||||||
|
alt_start,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
cx.tcx.emit_node_span_lint(
|
||||||
|
IF_LET_RESCOPE,
|
||||||
|
expr.hir_id,
|
||||||
|
span,
|
||||||
|
IfLetRescopeRewrite {
|
||||||
|
significant_dropper,
|
||||||
|
lifetime_end,
|
||||||
|
sugg: Some(IfLetRescopeRewriteSuggestion::WithoutElse {
|
||||||
|
if_let_pat,
|
||||||
|
before_conseq,
|
||||||
|
pat,
|
||||||
|
expr_end,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(LintDiagnostic)]
|
||||||
|
#[diag(lint_if_let_rescope)]
|
||||||
|
struct IfLetRescopeRewrite {
|
||||||
|
#[label]
|
||||||
|
significant_dropper: Span,
|
||||||
|
#[help]
|
||||||
|
lifetime_end: Span,
|
||||||
|
#[subdiagnostic]
|
||||||
|
sugg: Option<IfLetRescopeRewriteSuggestion>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Subdiagnostic)]
|
||||||
|
enum IfLetRescopeRewriteSuggestion {
|
||||||
|
#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
|
||||||
|
WithElse {
|
||||||
|
#[suggestion_part(code = "match ")]
|
||||||
|
if_let_pat: Span,
|
||||||
|
#[suggestion_part(code = " {{ {pat} => ")]
|
||||||
|
before_conseq: Span,
|
||||||
|
pat: String,
|
||||||
|
#[suggestion_part(code = "}}")]
|
||||||
|
expr_end: Span,
|
||||||
|
#[suggestion_part(code = " _ => ")]
|
||||||
|
alt_start: Span,
|
||||||
|
},
|
||||||
|
#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")]
|
||||||
|
WithoutElse {
|
||||||
|
#[suggestion_part(code = "match ")]
|
||||||
|
if_let_pat: Span,
|
||||||
|
#[suggestion_part(code = " {{ {pat} => ")]
|
||||||
|
before_conseq: Span,
|
||||||
|
pat: String,
|
||||||
|
#[suggestion_part(code = " _ => {{}} }}")]
|
||||||
|
expr_end: Span,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FindSignificantDropper<'tcx, 'a> {
|
||||||
|
cx: &'a LateContext<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> {
|
||||||
|
type Result = ControlFlow<Span>;
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
|
||||||
|
if self
|
||||||
|
.cx
|
||||||
|
.typeck_results()
|
||||||
|
.expr_ty(expr)
|
||||||
|
.has_significant_drop(self.cx.tcx, self.cx.param_env)
|
||||||
|
{
|
||||||
|
return ControlFlow::Break(expr.span);
|
||||||
|
}
|
||||||
|
match expr.kind {
|
||||||
|
hir::ExprKind::ConstBlock(_)
|
||||||
|
| hir::ExprKind::Lit(_)
|
||||||
|
| hir::ExprKind::Path(_)
|
||||||
|
| hir::ExprKind::Assign(_, _, _)
|
||||||
|
| hir::ExprKind::AssignOp(_, _, _)
|
||||||
|
| hir::ExprKind::Break(_, _)
|
||||||
|
| hir::ExprKind::Continue(_)
|
||||||
|
| hir::ExprKind::Ret(_)
|
||||||
|
| hir::ExprKind::Become(_)
|
||||||
|
| hir::ExprKind::InlineAsm(_)
|
||||||
|
| hir::ExprKind::OffsetOf(_, _)
|
||||||
|
| hir::ExprKind::Repeat(_, _)
|
||||||
|
| hir::ExprKind::Err(_)
|
||||||
|
| hir::ExprKind::Struct(_, _, _)
|
||||||
|
| hir::ExprKind::Closure(_)
|
||||||
|
| hir::ExprKind::Block(_, _)
|
||||||
|
| hir::ExprKind::DropTemps(_)
|
||||||
|
| hir::ExprKind::Loop(_, _, _, _) => ControlFlow::Continue(()),
|
||||||
|
|
||||||
|
hir::ExprKind::Tup(exprs) | hir::ExprKind::Array(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
self.visit_expr(expr)?;
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
hir::ExprKind::Call(callee, args) => {
|
||||||
|
self.visit_expr(callee)?;
|
||||||
|
for expr in args {
|
||||||
|
self.visit_expr(expr)?;
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
hir::ExprKind::MethodCall(_, receiver, args, _) => {
|
||||||
|
self.visit_expr(receiver)?;
|
||||||
|
for expr in args {
|
||||||
|
self.visit_expr(expr)?;
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
hir::ExprKind::Index(left, right, _) | hir::ExprKind::Binary(_, left, right) => {
|
||||||
|
self.visit_expr(left)?;
|
||||||
|
self.visit_expr(right)
|
||||||
|
}
|
||||||
|
hir::ExprKind::Unary(_, expr)
|
||||||
|
| hir::ExprKind::Cast(expr, _)
|
||||||
|
| hir::ExprKind::Type(expr, _)
|
||||||
|
| hir::ExprKind::Yield(expr, _)
|
||||||
|
| hir::ExprKind::AddrOf(_, _, expr)
|
||||||
|
| hir::ExprKind::Match(expr, _, _)
|
||||||
|
| hir::ExprKind::Field(expr, _)
|
||||||
|
| hir::ExprKind::Let(&hir::LetExpr {
|
||||||
|
init: expr,
|
||||||
|
span: _,
|
||||||
|
pat: _,
|
||||||
|
ty: _,
|
||||||
|
recovered: Recovered::No,
|
||||||
|
}) => self.visit_expr(expr),
|
||||||
|
hir::ExprKind::Let(_) => ControlFlow::Continue(()),
|
||||||
|
|
||||||
|
hir::ExprKind::If(cond, _, _) => {
|
||||||
|
if let hir::ExprKind::Let(hir::LetExpr {
|
||||||
|
init,
|
||||||
|
span: _,
|
||||||
|
pat: _,
|
||||||
|
ty: _,
|
||||||
|
recovered: Recovered::No,
|
||||||
|
}) = cond.kind
|
||||||
|
{
|
||||||
|
self.visit_expr(init)?;
|
||||||
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -56,6 +56,7 @@ mod expect;
|
|||||||
mod for_loops_over_fallibles;
|
mod for_loops_over_fallibles;
|
||||||
mod foreign_modules;
|
mod foreign_modules;
|
||||||
pub mod hidden_unicode_codepoints;
|
pub mod hidden_unicode_codepoints;
|
||||||
|
mod if_let_rescope;
|
||||||
mod impl_trait_overcaptures;
|
mod impl_trait_overcaptures;
|
||||||
mod internal;
|
mod internal;
|
||||||
mod invalid_from_utf8;
|
mod invalid_from_utf8;
|
||||||
@ -94,6 +95,7 @@ use drop_forget_useless::*;
|
|||||||
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
|
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
|
||||||
use for_loops_over_fallibles::*;
|
use for_loops_over_fallibles::*;
|
||||||
use hidden_unicode_codepoints::*;
|
use hidden_unicode_codepoints::*;
|
||||||
|
use if_let_rescope::IfLetRescope;
|
||||||
use impl_trait_overcaptures::ImplTraitOvercaptures;
|
use impl_trait_overcaptures::ImplTraitOvercaptures;
|
||||||
use internal::*;
|
use internal::*;
|
||||||
use invalid_from_utf8::*;
|
use invalid_from_utf8::*;
|
||||||
@ -243,6 +245,7 @@ late_lint_methods!(
|
|||||||
NonLocalDefinitions: NonLocalDefinitions::default(),
|
NonLocalDefinitions: NonLocalDefinitions::default(),
|
||||||
ImplTraitOvercaptures: ImplTraitOvercaptures,
|
ImplTraitOvercaptures: ImplTraitOvercaptures,
|
||||||
TailExprDropOrder: TailExprDropOrder,
|
TailExprDropOrder: TailExprDropOrder,
|
||||||
|
IfLetRescope: IfLetRescope,
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -96,6 +96,7 @@ impl fmt::Debug for Scope {
|
|||||||
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id),
|
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id),
|
||||||
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id),
|
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id),
|
||||||
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.id),
|
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.id),
|
||||||
|
ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.id),
|
||||||
ScopeData::Remainder(fsi) => write!(
|
ScopeData::Remainder(fsi) => write!(
|
||||||
fmt,
|
fmt,
|
||||||
"Remainder {{ block: {:?}, first_statement_index: {}}}",
|
"Remainder {{ block: {:?}, first_statement_index: {}}}",
|
||||||
@ -126,6 +127,11 @@ pub enum ScopeData {
|
|||||||
/// Used for variables introduced in an if-let expression.
|
/// Used for variables introduced in an if-let expression.
|
||||||
IfThen,
|
IfThen,
|
||||||
|
|
||||||
|
/// Scope of the condition and then block of an if expression
|
||||||
|
/// Used for variables introduced in an if-let expression,
|
||||||
|
/// whose lifetimes do not cross beyond this scope.
|
||||||
|
IfThenRescope,
|
||||||
|
|
||||||
/// Scope following a `let id = expr;` binding in a block.
|
/// Scope following a `let id = expr;` binding in a block.
|
||||||
Remainder(FirstStatementIndex),
|
Remainder(FirstStatementIndex),
|
||||||
}
|
}
|
||||||
|
@ -1084,6 +1084,9 @@ pub enum LocalInfo<'tcx> {
|
|||||||
/// (with no intervening statement context).
|
/// (with no intervening statement context).
|
||||||
// FIXME(matthewjasper) Don't store in this in `Body`
|
// FIXME(matthewjasper) Don't store in this in `Body`
|
||||||
BlockTailTemp(BlockTailInfo),
|
BlockTailTemp(BlockTailInfo),
|
||||||
|
/// A temporary created during evaluating `if` predicate, possibly for pattern matching for `let`s,
|
||||||
|
/// and subject to Edition 2024 temporary lifetime rules
|
||||||
|
IfThenRescopeTemp { if_then: HirId },
|
||||||
/// A temporary created during the pass `Derefer` to avoid it's retagging
|
/// A temporary created during the pass `Derefer` to avoid it's retagging
|
||||||
DerefTemp,
|
DerefTemp,
|
||||||
/// A temporary created for borrow checking.
|
/// A temporary created for borrow checking.
|
||||||
|
@ -41,7 +41,15 @@ impl RvalueScopes {
|
|||||||
debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
|
debug!("temporary_scope({expr_id:?}) = {id:?} [enclosing]");
|
||||||
return Some(id);
|
return Some(id);
|
||||||
}
|
}
|
||||||
_ => id = p,
|
ScopeData::IfThenRescope => {
|
||||||
|
debug!("temporary_scope({expr_id:?}) = {p:?} [enclosing]");
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
ScopeData::Node
|
||||||
|
| ScopeData::CallSite
|
||||||
|
| ScopeData::Arguments
|
||||||
|
| ScopeData::IfThen
|
||||||
|
| ScopeData::Remainder(_) => id = p,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
//! See docs in build/expr/mod.rs
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
|
use rustc_hir::HirId;
|
||||||
use rustc_middle::middle::region;
|
use rustc_middle::middle::region;
|
||||||
|
use rustc_middle::middle::region::{Scope, ScopeData};
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::thir::*;
|
use rustc_middle::thir::*;
|
||||||
use tracing::{debug, instrument};
|
use tracing::{debug, instrument};
|
||||||
@ -73,11 +75,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
_ if let Some(tail_info) = this.block_context.currently_in_block_tail() => {
|
_ if let Some(tail_info) = this.block_context.currently_in_block_tail() => {
|
||||||
LocalInfo::BlockTailTemp(tail_info)
|
LocalInfo::BlockTailTemp(tail_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ if let Some(Scope { data: ScopeData::IfThenRescope, id }) = temp_lifetime => {
|
||||||
|
LocalInfo::IfThenRescopeTemp {
|
||||||
|
if_then: HirId { owner: this.hir_id.owner, local_id: id },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_ => LocalInfo::Boring,
|
_ => LocalInfo::Boring,
|
||||||
};
|
};
|
||||||
**local_decl.local_info.as_mut().assert_crate_local() = local_info;
|
**local_decl.local_info.as_mut().assert_crate_local() = local_info;
|
||||||
this.local_decls.push(local_decl)
|
this.local_decls.push(local_decl)
|
||||||
};
|
};
|
||||||
|
debug!(?temp);
|
||||||
if deduplicate_temps {
|
if deduplicate_temps {
|
||||||
this.fixed_temps.insert(expr_id, temp);
|
this.fixed_temps.insert(expr_id, temp);
|
||||||
}
|
}
|
||||||
|
@ -706,7 +706,13 @@ impl<'tcx> Cx<'tcx> {
|
|||||||
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
||||||
if_then_scope: region::Scope {
|
if_then_scope: region::Scope {
|
||||||
id: then.hir_id.local_id,
|
id: then.hir_id.local_id,
|
||||||
data: region::ScopeData::IfThen,
|
data: {
|
||||||
|
if expr.span.at_least_rust_2024() && tcx.features().if_let_rescope {
|
||||||
|
region::ScopeData::IfThenRescope
|
||||||
|
} else {
|
||||||
|
region::ScopeData::IfThen
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
cond: self.mirror_expr(cond),
|
cond: self.mirror_expr(cond),
|
||||||
then: self.mirror_expr(then),
|
then: self.mirror_expr(then),
|
||||||
|
@ -1017,6 +1017,7 @@ symbols! {
|
|||||||
ident,
|
ident,
|
||||||
if_let,
|
if_let,
|
||||||
if_let_guard,
|
if_let_guard,
|
||||||
|
if_let_rescope,
|
||||||
if_while_or_patterns,
|
if_while_or_patterns,
|
||||||
ignore,
|
ignore,
|
||||||
impl_header_lifetime_elision,
|
impl_header_lifetime_elision,
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
//@ run-pass
|
//@ run-pass
|
||||||
//@ compile-flags: -Z validate-mir
|
//@ compile-flags: -Z validate-mir
|
||||||
|
//@ revisions: edition2021 edition2024
|
||||||
|
//@ [edition2021] edition: 2021
|
||||||
|
//@ [edition2024] compile-flags: -Z unstable-options
|
||||||
|
//@ [edition2024] edition: 2024
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![cfg_attr(edition2024, feature(if_let_rescope))]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
@ -55,11 +60,18 @@ impl DropOrderCollector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn if_let(&self) {
|
fn if_let(&self) {
|
||||||
|
#[cfg(edition2021)]
|
||||||
if let None = self.option_loud_drop(2) {
|
if let None = self.option_loud_drop(2) {
|
||||||
unreachable!();
|
unreachable!();
|
||||||
} else {
|
} else {
|
||||||
self.print(1);
|
self.print(1);
|
||||||
}
|
}
|
||||||
|
#[cfg(edition2024)]
|
||||||
|
if let None = self.option_loud_drop(1) {
|
||||||
|
unreachable!();
|
||||||
|
} else {
|
||||||
|
self.print(2);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(_) = self.option_loud_drop(4) {
|
if let Some(_) = self.option_loud_drop(4) {
|
||||||
self.print(3);
|
self.print(3);
|
||||||
@ -194,6 +206,7 @@ impl DropOrderCollector {
|
|||||||
self.print(3); // 3
|
self.print(3); // 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(edition2021)]
|
||||||
// take the "else" branch
|
// take the "else" branch
|
||||||
if self.option_loud_drop(5).is_some() // 1
|
if self.option_loud_drop(5).is_some() // 1
|
||||||
&& self.option_loud_drop(6).is_some() // 2
|
&& self.option_loud_drop(6).is_some() // 2
|
||||||
@ -202,6 +215,15 @@ impl DropOrderCollector {
|
|||||||
} else {
|
} else {
|
||||||
self.print(7); // 3
|
self.print(7); // 3
|
||||||
}
|
}
|
||||||
|
#[cfg(edition2024)]
|
||||||
|
// take the "else" branch
|
||||||
|
if self.option_loud_drop(5).is_some() // 1
|
||||||
|
&& self.option_loud_drop(6).is_some() // 2
|
||||||
|
&& let None = self.option_loud_drop(7) { // 4
|
||||||
|
unreachable!();
|
||||||
|
} else {
|
||||||
|
self.print(8); // 3
|
||||||
|
}
|
||||||
|
|
||||||
// let exprs interspersed
|
// let exprs interspersed
|
||||||
if self.option_loud_drop(9).is_some() // 1
|
if self.option_loud_drop(9).is_some() // 1
|
||||||
|
122
tests/ui/drop/drop_order_if_let_rescope.rs
Normal file
122
tests/ui/drop/drop_order_if_let_rescope.rs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
//@ run-pass
|
||||||
|
//@ edition:2024
|
||||||
|
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||||
|
|
||||||
|
#![feature(let_chains)]
|
||||||
|
#![feature(if_let_rescope)]
|
||||||
|
|
||||||
|
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 print(&self, n: u32) {
|
||||||
|
println!("{}", n);
|
||||||
|
self.0.borrow_mut().push(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_sorted(self) {
|
||||||
|
assert!(
|
||||||
|
self.0
|
||||||
|
.into_inner()
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.all(|(idx, item)| idx + 1 == item.try_into().unwrap())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn if_let(&self) {
|
||||||
|
if let None = self.option_loud_drop(1) {
|
||||||
|
unreachable!();
|
||||||
|
} else {
|
||||||
|
self.print(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_) = self.option_loud_drop(4) {
|
||||||
|
self.print(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(_d) = self.option_loud_drop(6) {
|
||||||
|
self.print(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn let_chain(&self) {
|
||||||
|
// take the "then" branch
|
||||||
|
if self.option_loud_drop(1).is_some() // 1
|
||||||
|
&& self.option_loud_drop(2).is_some() // 2
|
||||||
|
&& let Some(_d) = self.option_loud_drop(4)
|
||||||
|
// 4
|
||||||
|
{
|
||||||
|
self.print(3); // 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// take the "else" branch
|
||||||
|
if self.option_loud_drop(5).is_some() // 1
|
||||||
|
&& self.option_loud_drop(6).is_some() // 2
|
||||||
|
&& let None = self.option_loud_drop(7)
|
||||||
|
// 3
|
||||||
|
{
|
||||||
|
unreachable!();
|
||||||
|
} else {
|
||||||
|
self.print(8); // 4
|
||||||
|
}
|
||||||
|
|
||||||
|
// let exprs interspersed
|
||||||
|
if self.option_loud_drop(9).is_some() // 1
|
||||||
|
&& let Some(_d) = self.option_loud_drop(13) // 5
|
||||||
|
&& self.option_loud_drop(10).is_some() // 2
|
||||||
|
&& let Some(_e) = self.option_loud_drop(12)
|
||||||
|
// 4
|
||||||
|
{
|
||||||
|
self.print(11); // 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// let exprs first
|
||||||
|
if let Some(_d) = self.option_loud_drop(18) // 5
|
||||||
|
&& let Some(_e) = self.option_loud_drop(17) // 4
|
||||||
|
&& self.option_loud_drop(14).is_some() // 1
|
||||||
|
&& self.option_loud_drop(15).is_some()
|
||||||
|
// 2
|
||||||
|
{
|
||||||
|
self.print(16); // 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// let exprs last
|
||||||
|
if self.option_loud_drop(19).is_some() // 1
|
||||||
|
&& self.option_loud_drop(20).is_some() // 2
|
||||||
|
&& let Some(_d) = self.option_loud_drop(23) // 5
|
||||||
|
&& let Some(_e) = self.option_loud_drop(22)
|
||||||
|
// 4
|
||||||
|
{
|
||||||
|
self.print(21); // 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("-- if let --");
|
||||||
|
let collector = DropOrderCollector::default();
|
||||||
|
collector.if_let();
|
||||||
|
collector.assert_sorted();
|
||||||
|
|
||||||
|
println!("-- let chain --");
|
||||||
|
let collector = DropOrderCollector::default();
|
||||||
|
collector.let_chain();
|
||||||
|
collector.assert_sorted();
|
||||||
|
}
|
26
tests/ui/drop/if-let-rescope-borrowck-suggestions.fixed
Normal file
26
tests/ui/drop/if-let-rescope-borrowck-suggestions.fixed
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//@ edition: 2024
|
||||||
|
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||||
|
//@ run-rustfix
|
||||||
|
|
||||||
|
#![feature(if_let_rescope)]
|
||||||
|
#![deny(if_let_rescope)]
|
||||||
|
|
||||||
|
struct Droppy;
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Droppy {
|
||||||
|
fn get_ref(&self) -> Option<&u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_something<T>(_: &T) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let binding = Droppy;
|
||||||
|
do_something(match binding.get_ref() { Some(value) => { value } _ => { &0 }});
|
||||||
|
//~^ ERROR: temporary value dropped while borrowed
|
||||||
|
}
|
25
tests/ui/drop/if-let-rescope-borrowck-suggestions.rs
Normal file
25
tests/ui/drop/if-let-rescope-borrowck-suggestions.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//@ edition: 2024
|
||||||
|
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||||
|
//@ run-rustfix
|
||||||
|
|
||||||
|
#![feature(if_let_rescope)]
|
||||||
|
#![deny(if_let_rescope)]
|
||||||
|
|
||||||
|
struct Droppy;
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Droppy {
|
||||||
|
fn get_ref(&self) -> Option<&u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn do_something<T>(_: &T) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
do_something(if let Some(value) = Droppy.get_ref() { value } else { &0 });
|
||||||
|
//~^ ERROR: temporary value dropped while borrowed
|
||||||
|
}
|
26
tests/ui/drop/if-let-rescope-borrowck-suggestions.stderr
Normal file
26
tests/ui/drop/if-let-rescope-borrowck-suggestions.stderr
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
error[E0716]: temporary value dropped while borrowed
|
||||||
|
--> $DIR/if-let-rescope-borrowck-suggestions.rs:23:39
|
||||||
|
|
|
||||||
|
LL | do_something(if let Some(value) = Droppy.get_ref() { value } else { &0 });
|
||||||
|
| ^^^^^^ - temporary value is freed at the end of this statement
|
||||||
|
| |
|
||||||
|
| creates a temporary value which is freed while still in use
|
||||||
|
|
|
||||||
|
note: lifetime for temporaries generated in `if let`s have been shorted in Edition 2024
|
||||||
|
--> $DIR/if-let-rescope-borrowck-suggestions.rs:23:65
|
||||||
|
|
|
||||||
|
LL | do_something(if let Some(value) = Droppy.get_ref() { value } else { &0 });
|
||||||
|
| ^
|
||||||
|
help: consider using a `let` binding to create a longer lived value
|
||||||
|
|
|
||||||
|
LL ~ let binding = Droppy;
|
||||||
|
LL ~ do_something(if let Some(value) = binding.get_ref() { value } else { &0 });
|
||||||
|
|
|
||||||
|
help: consider rewriting the `if` into `match` which preserves the extended lifetime
|
||||||
|
|
|
||||||
|
LL | do_something(match Droppy.get_ref() { Some(value) => { value } _ => { &0 }});
|
||||||
|
| ~~~~~ ++++++++++++++++ ~~~~ +
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0716`.
|
31
tests/ui/drop/lint-if-let-rescope-gated.rs
Normal file
31
tests/ui/drop/lint-if-let-rescope-gated.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// This test checks that the lint `if_let_rescope` only actions
|
||||||
|
// when the feature gate is enabled.
|
||||||
|
// Edition 2021 is used here because the lint should work especially
|
||||||
|
// when edition migration towards 2024 is run.
|
||||||
|
|
||||||
|
//@ revisions: with_feature_gate without_feature_gate
|
||||||
|
//@ [without_feature_gate] check-pass
|
||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
#![cfg_attr(with_feature_gate, feature(if_let_rescope))]
|
||||||
|
#![deny(if_let_rescope)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
|
struct Droppy;
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Droppy {
|
||||||
|
fn get(&self) -> Option<u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Some(_value) = Droppy.get() {
|
||||||
|
//[with_feature_gate]~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//[with_feature_gate]~| WARN: this changes meaning in Rust 2024
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
error: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope-gated.rs:27:8
|
||||||
|
|
|
||||||
|
LL | if let Some(_value) = Droppy.get() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^------^^^^^^
|
||||||
|
| |
|
||||||
|
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
|
||||||
|
help: the value is now dropped here in Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope-gated.rs:30:5
|
||||||
|
|
|
||||||
|
LL | };
|
||||||
|
| ^
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/lint-if-let-rescope-gated.rs:11:9
|
||||||
|
|
|
||||||
|
LL | #![deny(if_let_rescope)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021
|
||||||
|
|
|
||||||
|
LL ~ match Droppy.get() { Some(_value) => {
|
||||||
|
LL |
|
||||||
|
LL |
|
||||||
|
LL ~ } _ => {} };
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
41
tests/ui/drop/lint-if-let-rescope-with-macro.rs
Normal file
41
tests/ui/drop/lint-if-let-rescope-with-macro.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// This test ensures that no suggestion is emitted if the span originates from
|
||||||
|
// an expansion that is probably not under a user's control.
|
||||||
|
|
||||||
|
//@ edition:2021
|
||||||
|
//@ compile-flags: -Z unstable-options
|
||||||
|
|
||||||
|
#![feature(if_let_rescope)]
|
||||||
|
#![deny(if_let_rescope)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
|
macro_rules! edition_2021_if_let {
|
||||||
|
($p:pat, $e:expr, { $($conseq:tt)* } { $($alt:tt)* }) => {
|
||||||
|
if let $p = $e { $($conseq)* } else { $($alt)* }
|
||||||
|
//~^ ERROR `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN this changes meaning in Rust 2024
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn droppy() -> Droppy {
|
||||||
|
Droppy
|
||||||
|
}
|
||||||
|
struct Droppy;
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Droppy {
|
||||||
|
fn get(&self) -> Option<u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
edition_2021_if_let! {
|
||||||
|
Some(_value),
|
||||||
|
droppy().get(),
|
||||||
|
{}
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
}
|
39
tests/ui/drop/lint-if-let-rescope-with-macro.stderr
Normal file
39
tests/ui/drop/lint-if-let-rescope-with-macro.stderr
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
error: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope-with-macro.rs:13:12
|
||||||
|
|
|
||||||
|
LL | if let $p = $e { $($conseq)* } else { $($alt)* }
|
||||||
|
| ^^^
|
||||||
|
...
|
||||||
|
LL | / edition_2021_if_let! {
|
||||||
|
LL | | Some(_value),
|
||||||
|
LL | | droppy().get(),
|
||||||
|
| | -------- this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
||||||
|
LL | | {}
|
||||||
|
LL | | {}
|
||||||
|
LL | | };
|
||||||
|
| |_____- in this macro invocation
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
|
||||||
|
help: the value is now dropped here in Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope-with-macro.rs:13:38
|
||||||
|
|
|
||||||
|
LL | if let $p = $e { $($conseq)* } else { $($alt)* }
|
||||||
|
| ^
|
||||||
|
...
|
||||||
|
LL | / edition_2021_if_let! {
|
||||||
|
LL | | Some(_value),
|
||||||
|
LL | | droppy().get(),
|
||||||
|
LL | | {}
|
||||||
|
LL | | {}
|
||||||
|
LL | | };
|
||||||
|
| |_____- in this macro invocation
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/lint-if-let-rescope-with-macro.rs:8:9
|
||||||
|
|
|
||||||
|
LL | #![deny(if_let_rescope)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
= note: this error originates in the macro `edition_2021_if_let` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
48
tests/ui/drop/lint-if-let-rescope.fixed
Normal file
48
tests/ui/drop/lint-if-let-rescope.fixed
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//@ edition:2024
|
||||||
|
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||||
|
//@ run-rustfix
|
||||||
|
|
||||||
|
#![feature(if_let_rescope)]
|
||||||
|
#![deny(if_let_rescope)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
|
fn droppy() -> Droppy {
|
||||||
|
Droppy
|
||||||
|
}
|
||||||
|
struct Droppy;
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Droppy {
|
||||||
|
fn get(&self) -> Option<u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
match droppy().get() { Some(_value) => {
|
||||||
|
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| HELP: rewrite this `if let` into a `match`
|
||||||
|
// do something
|
||||||
|
} _ => {
|
||||||
|
//~^ HELP: the value is now dropped here in Edition 2024
|
||||||
|
// do something else
|
||||||
|
}}
|
||||||
|
|
||||||
|
if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } {
|
||||||
|
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| HELP: rewrite this `if let` into a `match`
|
||||||
|
//~| HELP: the value is now dropped here in Edition 2024
|
||||||
|
}
|
||||||
|
|
||||||
|
if let () = { match Droppy.get() { Some(_value) => {} _ => {} } } {
|
||||||
|
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| HELP: rewrite this `if let` into a `match`
|
||||||
|
//~| HELP: the value is now dropped here in Edition 2024
|
||||||
|
}
|
||||||
|
}
|
48
tests/ui/drop/lint-if-let-rescope.rs
Normal file
48
tests/ui/drop/lint-if-let-rescope.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//@ edition:2024
|
||||||
|
//@ compile-flags: -Z validate-mir -Zunstable-options
|
||||||
|
//@ run-rustfix
|
||||||
|
|
||||||
|
#![feature(if_let_rescope)]
|
||||||
|
#![deny(if_let_rescope)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
|
fn droppy() -> Droppy {
|
||||||
|
Droppy
|
||||||
|
}
|
||||||
|
struct Droppy;
|
||||||
|
impl Drop for Droppy {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
println!("dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Droppy {
|
||||||
|
fn get(&self) -> Option<u8> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
if let Some(_value) = droppy().get() {
|
||||||
|
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| HELP: rewrite this `if let` into a `match`
|
||||||
|
// do something
|
||||||
|
} else {
|
||||||
|
//~^ HELP: the value is now dropped here in Edition 2024
|
||||||
|
// do something else
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } {
|
||||||
|
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| HELP: rewrite this `if let` into a `match`
|
||||||
|
//~| HELP: the value is now dropped here in Edition 2024
|
||||||
|
}
|
||||||
|
|
||||||
|
if let () = { if let Some(_value) = Droppy.get() {} } {
|
||||||
|
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
//~| WARN: this changes meaning in Rust 2024
|
||||||
|
//~| HELP: rewrite this `if let` into a `match`
|
||||||
|
//~| HELP: the value is now dropped here in Edition 2024
|
||||||
|
}
|
||||||
|
}
|
74
tests/ui/drop/lint-if-let-rescope.stderr
Normal file
74
tests/ui/drop/lint-if-let-rescope.stderr
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
error: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:25:8
|
||||||
|
|
|
||||||
|
LL | if let Some(_value) = droppy().get() {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^--------^^^^^^
|
||||||
|
| |
|
||||||
|
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
|
||||||
|
help: the value is now dropped here in Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:30:5
|
||||||
|
|
|
||||||
|
LL | } else {
|
||||||
|
| ^
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:6:9
|
||||||
|
|
|
||||||
|
LL | #![deny(if_let_rescope)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021
|
||||||
|
|
|
||||||
|
LL ~ match droppy().get() { Some(_value) => {
|
||||||
|
LL |
|
||||||
|
...
|
||||||
|
LL | // do something
|
||||||
|
LL ~ } _ => {
|
||||||
|
LL |
|
||||||
|
LL | // do something else
|
||||||
|
LL ~ }}
|
||||||
|
|
|
||||||
|
|
||||||
|
error: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:35:27
|
||||||
|
|
|
||||||
|
LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^------^^^^^^
|
||||||
|
| |
|
||||||
|
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
|
||||||
|
help: the value is now dropped here in Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:35:69
|
||||||
|
|
|
||||||
|
LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } {
|
||||||
|
| ^
|
||||||
|
help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021
|
||||||
|
|
|
||||||
|
LL | if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } {
|
||||||
|
| ~~~~~ +++++++++++++++++ ~~~~ +
|
||||||
|
|
||||||
|
error: `if let` assigns a shorter lifetime since Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:42:22
|
||||||
|
|
|
||||||
|
LL | if let () = { if let Some(_value) = Droppy.get() {} } {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^------^^^^^^
|
||||||
|
| |
|
||||||
|
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
|
||||||
|
|
|
||||||
|
= warning: this changes meaning in Rust 2024
|
||||||
|
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
|
||||||
|
help: the value is now dropped here in Edition 2024
|
||||||
|
--> $DIR/lint-if-let-rescope.rs:42:55
|
||||||
|
|
|
||||||
|
LL | if let () = { if let Some(_value) = Droppy.get() {} } {
|
||||||
|
| ^
|
||||||
|
help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021
|
||||||
|
|
|
||||||
|
LL | if let () = { match Droppy.get() { Some(_value) => {} _ => {} } } {
|
||||||
|
| ~~~~~ +++++++++++++++++ +++++++++
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
27
tests/ui/feature-gates/feature-gate-if-let-rescope.rs
Normal file
27
tests/ui/feature-gates/feature-gate-if-let-rescope.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// This test shows the code that could have been accepted by enabling #![feature(if_let_rescope)]
|
||||||
|
|
||||||
|
struct A;
|
||||||
|
struct B<'a, T>(&'a mut T);
|
||||||
|
|
||||||
|
impl A {
|
||||||
|
fn f(&mut self) -> Option<B<'_, Self>> {
|
||||||
|
Some(B(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Drop for B<'a, T> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// this is needed to keep NLL's hands off and to ensure
|
||||||
|
// the inner mutable borrow stays alive
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut a = A;
|
||||||
|
if let None = a.f().as_ref() {
|
||||||
|
unreachable!()
|
||||||
|
} else {
|
||||||
|
a.f().unwrap();
|
||||||
|
//~^ ERROR cannot borrow `a` as mutable more than once at a time
|
||||||
|
};
|
||||||
|
}
|
18
tests/ui/feature-gates/feature-gate-if-let-rescope.stderr
Normal file
18
tests/ui/feature-gates/feature-gate-if-let-rescope.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
error[E0499]: cannot borrow `a` as mutable more than once at a time
|
||||||
|
--> $DIR/feature-gate-if-let-rescope.rs:24:9
|
||||||
|
|
|
||||||
|
LL | if let None = a.f().as_ref() {
|
||||||
|
| -----
|
||||||
|
| |
|
||||||
|
| first mutable borrow occurs here
|
||||||
|
| a temporary with access to the first borrow is created here ...
|
||||||
|
...
|
||||||
|
LL | a.f().unwrap();
|
||||||
|
| ^ second mutable borrow occurs here
|
||||||
|
LL |
|
||||||
|
LL | };
|
||||||
|
| - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option<B<'_, A>>`
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0499`.
|
@ -1,9 +1,14 @@
|
|||||||
//@ run-pass
|
//@ run-pass
|
||||||
//@ needs-unwind
|
//@ needs-unwind
|
||||||
|
//@ revisions: edition2021 edition2024
|
||||||
|
//@ [edition2021] edition: 2021
|
||||||
|
//@ [edition2024] compile-flags: -Z unstable-options
|
||||||
|
//@ [edition2024] edition: 2024
|
||||||
|
|
||||||
// See `mir_drop_order.rs` for more information
|
// See `mir_drop_order.rs` for more information
|
||||||
|
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
|
#![cfg_attr(edition2024, feature(if_let_rescope))]
|
||||||
#![allow(irrefutable_let_patterns)]
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -39,25 +44,32 @@ fn main() {
|
|||||||
0,
|
0,
|
||||||
d(
|
d(
|
||||||
1,
|
1,
|
||||||
if let Some(_) = d(2, Some(true)).extra && let DropLogger { .. } = d(3, None) {
|
if let Some(_) = d(2, Some(true)).extra
|
||||||
|
&& let DropLogger { .. } = d(3, None)
|
||||||
|
{
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
},
|
||||||
).extra
|
)
|
||||||
|
.extra,
|
||||||
),
|
),
|
||||||
d(4, None),
|
d(4, None),
|
||||||
&d(5, None),
|
&d(5, None),
|
||||||
d(6, None),
|
d(6, None),
|
||||||
if let DropLogger { .. } = d(7, None) && let DropLogger { .. } = d(8, None) {
|
if let DropLogger { .. } = d(7, None)
|
||||||
|
&& let DropLogger { .. } = d(8, None)
|
||||||
|
{
|
||||||
d(9, None)
|
d(9, None)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// 10 is not constructed
|
// 10 is not constructed
|
||||||
d(10, None)
|
d(10, None)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
#[cfg(edition2021)]
|
||||||
assert_eq!(get(), vec![8, 7, 1, 3, 2]);
|
assert_eq!(get(), vec![8, 7, 1, 3, 2]);
|
||||||
|
#[cfg(edition2024)]
|
||||||
|
assert_eq!(get(), vec![3, 2, 8, 7, 1]);
|
||||||
}
|
}
|
||||||
assert_eq!(get(), vec![0, 4, 6, 9, 5]);
|
assert_eq!(get(), vec![0, 4, 6, 9, 5]);
|
||||||
|
|
||||||
@ -73,21 +85,26 @@ fn main() {
|
|||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(true)
|
Some(true)
|
||||||
}
|
},
|
||||||
).extra
|
)
|
||||||
|
.extra,
|
||||||
),
|
),
|
||||||
d(15, None),
|
d(15, None),
|
||||||
&d(16, None),
|
&d(16, None),
|
||||||
d(17, None),
|
d(17, None),
|
||||||
if let DropLogger { .. } = d(18, None) && let DropLogger { .. } = d(19, None) {
|
if let DropLogger { .. } = d(18, None)
|
||||||
|
&& let DropLogger { .. } = d(19, None)
|
||||||
|
{
|
||||||
d(20, None)
|
d(20, None)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// 10 is not constructed
|
// 10 is not constructed
|
||||||
d(21, None)
|
d(21, None)
|
||||||
},
|
},
|
||||||
panic::panic_any(InjectedFailure)
|
panic::panic_any(InjectedFailure),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
#[cfg(edition2021)]
|
||||||
assert_eq!(get(), vec![20, 17, 15, 11, 19, 18, 16, 12, 14, 13]);
|
assert_eq!(get(), vec![20, 17, 15, 11, 19, 18, 16, 12, 14, 13]);
|
||||||
|
#[cfg(edition2024)]
|
||||||
|
assert_eq!(get(), vec![14, 13, 19, 18, 20, 17, 15, 11, 16, 12]);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0597]: `counter` does not live long enough
|
error[E0597]: `counter` does not live long enough
|
||||||
--> $DIR/issue-54556-niconii.rs:22:20
|
--> $DIR/issue-54556-niconii.rs:30:20
|
||||||
|
|
|
|
||||||
LL | let counter = Mutex;
|
LL | let counter = Mutex;
|
||||||
| ------- binding `counter` declared here
|
| ------- binding `counter` declared here
|
@ -6,6 +6,14 @@
|
|||||||
// of temp drop order, and thus why inserting a semi-colon after the
|
// of temp drop order, and thus why inserting a semi-colon after the
|
||||||
// `if let` expression in `main` works.
|
// `if let` expression in `main` works.
|
||||||
|
|
||||||
|
//@ revisions: edition2021 edition2024
|
||||||
|
//@ [edition2021] edition: 2021
|
||||||
|
//@ [edition2024] edition: 2024
|
||||||
|
//@ [edition2024] compile-flags: -Z unstable-options
|
||||||
|
//@ [edition2024] check-pass
|
||||||
|
|
||||||
|
#![cfg_attr(edition2024, feature(if_let_rescope))]
|
||||||
|
|
||||||
struct Mutex;
|
struct Mutex;
|
||||||
struct MutexGuard<'a>(&'a Mutex);
|
struct MutexGuard<'a>(&'a Mutex);
|
||||||
|
|
||||||
@ -19,8 +27,10 @@ impl Mutex {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let counter = Mutex;
|
let counter = Mutex;
|
||||||
|
|
||||||
if let Ok(_) = counter.lock() { } //~ ERROR does not live long enough
|
if let Ok(_) = counter.lock() { }
|
||||||
|
//[edition2021]~^ ERROR: does not live long enough
|
||||||
|
|
||||||
|
// Up until Edition 2021:
|
||||||
// With this code as written, the dynamic semantics here implies
|
// With this code as written, the dynamic semantics here implies
|
||||||
// that `Mutex::drop` for `counter` runs *before*
|
// that `Mutex::drop` for `counter` runs *before*
|
||||||
// `MutexGuard::drop`, which would be unsound since `MutexGuard`
|
// `MutexGuard::drop`, which would be unsound since `MutexGuard`
|
||||||
@ -28,4 +38,11 @@ fn main() {
|
|||||||
//
|
//
|
||||||
// The goal of #54556 is to explain that within a compiler
|
// The goal of #54556 is to explain that within a compiler
|
||||||
// diagnostic.
|
// diagnostic.
|
||||||
|
|
||||||
|
// From Edition 2024:
|
||||||
|
// Now `MutexGuard::drop` runs *before* `Mutex::drop` because
|
||||||
|
// the lifetime of the `MutexGuard` is shortened to cover only
|
||||||
|
// from `if let` until the end of the consequent block.
|
||||||
|
// Therefore, Niconii's issue is properly solved thanks to the new
|
||||||
|
// temporary lifetime rule for `if let`s.
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user