Formally implement let chains
This commit is contained in:
parent
9ad5d82f82
commit
5f74ef4fb1
31 changed files with 536 additions and 340 deletions
|
@ -90,17 +90,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
};
|
||||
|
||||
let join_block = this.cfg.start_new_block();
|
||||
this.cfg.terminate(
|
||||
then_blk,
|
||||
source_info,
|
||||
TerminatorKind::Goto { target: join_block },
|
||||
);
|
||||
this.cfg.terminate(
|
||||
else_blk,
|
||||
source_info,
|
||||
TerminatorKind::Goto { target: join_block },
|
||||
);
|
||||
|
||||
this.cfg.goto(then_blk, source_info, join_block);
|
||||
this.cfg.goto(else_blk, source_info, join_block);
|
||||
join_block.unit()
|
||||
}
|
||||
ExprKind::Let { expr, ref pat } => {
|
||||
|
@ -109,8 +100,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
this.lower_let_expr(block, &this.thir[expr], pat, scope, expr_span)
|
||||
});
|
||||
|
||||
let join_block = this.cfg.start_new_block();
|
||||
|
||||
this.cfg.push_assign_constant(
|
||||
true_block,
|
||||
source_info,
|
||||
|
@ -133,6 +122,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
},
|
||||
);
|
||||
|
||||
let join_block = this.cfg.start_new_block();
|
||||
this.cfg.goto(true_block, source_info, join_block);
|
||||
this.cfg.goto(false_block, source_info, join_block);
|
||||
join_block.unit()
|
||||
|
|
|
@ -47,6 +47,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let expr_span = expr.span;
|
||||
|
||||
match expr.kind {
|
||||
ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
|
||||
let lhs_then_block = unpack!(this.then_else_break(
|
||||
block,
|
||||
&this.thir[lhs],
|
||||
temp_scope_override,
|
||||
break_scope,
|
||||
variable_scope_span,
|
||||
));
|
||||
|
||||
let rhs_then_block = unpack!(this.then_else_break(
|
||||
lhs_then_block,
|
||||
&this.thir[rhs],
|
||||
temp_scope_override,
|
||||
break_scope,
|
||||
variable_scope_span,
|
||||
));
|
||||
|
||||
rhs_then_block.unit()
|
||||
}
|
||||
ExprKind::Scope { region_scope, lint_level, value } => {
|
||||
let region_scope = (region_scope, this.source_info(expr_span));
|
||||
this.in_scope(region_scope, lint_level, |this| {
|
||||
|
|
|
@ -498,7 +498,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
///
|
||||
/// if let Some(x) = a && let Some(y) = b && let Some(z) = c { ... }
|
||||
///
|
||||
/// there are three possible ways the condition can be false and we may have
|
||||
/// There are three possible ways the condition can be false and we may have
|
||||
/// to drop `x`, `x` and `y`, or neither depending on which binding fails.
|
||||
/// To handle this correctly we use a `DropTree` in a similar way to a
|
||||
/// `loop` expression and 'break' out on all of the 'else' paths.
|
||||
|
|
|
@ -315,7 +315,6 @@ impl<'tcx> Cx<'tcx> {
|
|||
lhs: self.mirror_expr(lhs),
|
||||
rhs: self.mirror_expr(rhs),
|
||||
},
|
||||
|
||||
_ => {
|
||||
let op = bin_op(op.node);
|
||||
ExprKind::Binary {
|
||||
|
|
|
@ -17,6 +17,7 @@ use rustc_session::lint::builtin::{
|
|||
BINDINGS_WITH_VARIANT_NAME, IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS,
|
||||
};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{DesugaringKind, ExpnKind, Span};
|
||||
|
||||
crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
|
||||
|
@ -445,6 +446,10 @@ fn check_let_reachability<'p, 'tcx>(
|
|||
pat: &'p DeconstructedPat<'p, 'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
if is_let_chain(cx.tcx, pat_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
|
||||
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty());
|
||||
|
||||
|
@ -764,8 +769,11 @@ pub enum LetSource {
|
|||
|
||||
fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
|
||||
let hir = tcx.hir();
|
||||
|
||||
let parent = hir.get_parent_node(pat_id);
|
||||
match hir.get(parent) {
|
||||
let parent_node = hir.get(parent);
|
||||
|
||||
match parent_node {
|
||||
hir::Node::Arm(hir::Arm {
|
||||
guard: Some(hir::Guard::IfLet(&hir::Pat { hir_id, .. }, _)),
|
||||
..
|
||||
|
@ -780,6 +788,7 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let parent_parent = hir.get_parent_node(parent);
|
||||
let parent_parent_node = hir.get(parent_parent);
|
||||
|
||||
|
@ -792,12 +801,30 @@ fn let_source(tcx: TyCtxt<'_>, pat_id: HirId) -> LetSource {
|
|||
..
|
||||
}) = parent_parent_parent_parent_node
|
||||
{
|
||||
LetSource::WhileLet
|
||||
} else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
|
||||
parent_parent_node
|
||||
{
|
||||
LetSource::IfLet
|
||||
} else {
|
||||
LetSource::GenericLet
|
||||
return LetSource::WhileLet;
|
||||
}
|
||||
|
||||
if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If(..), .. }) = parent_parent_node {
|
||||
return LetSource::IfLet;
|
||||
}
|
||||
|
||||
LetSource::GenericLet
|
||||
}
|
||||
|
||||
// Since this function is called within a let context, it is reasonable to assume that any parent
|
||||
// `&&` infers a let chain
|
||||
fn is_let_chain(tcx: TyCtxt<'_>, pat_id: HirId) -> bool {
|
||||
let hir = tcx.hir();
|
||||
let parent = hir.get_parent_node(pat_id);
|
||||
let parent_parent = hir.get_parent_node(parent);
|
||||
matches!(
|
||||
hir.get(parent_parent),
|
||||
hir::Node::Expr(
|
||||
hir::Expr {
|
||||
kind: hir::ExprKind::Binary(Spanned { node: hir::BinOpKind::And, .. }, ..),
|
||||
..
|
||||
},
|
||||
..
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue