Implement lowering of if-let guards to MIR
This commit is contained in:
parent
bab20800f0
commit
f3d4aa6afb
7 changed files with 84 additions and 11 deletions
|
@ -1722,6 +1722,8 @@ pub enum MatchSource {
|
||||||
IfDesugar { contains_else_clause: bool },
|
IfDesugar { contains_else_clause: bool },
|
||||||
/// An `if let _ = _ { .. }` (optionally with `else { .. }`).
|
/// An `if let _ = _ { .. }` (optionally with `else { .. }`).
|
||||||
IfLetDesugar { contains_else_clause: bool },
|
IfLetDesugar { contains_else_clause: bool },
|
||||||
|
/// An `if let _ = _ => { .. }` match guard.
|
||||||
|
IfLetGuardDesugar,
|
||||||
/// A `while _ { .. }` (which was desugared to a `loop { match _ { .. } }`).
|
/// A `while _ { .. }` (which was desugared to a `loop { match _ { .. } }`).
|
||||||
WhileDesugar,
|
WhileDesugar,
|
||||||
/// A `while let _ = _ { .. }` (which was desugared to a
|
/// A `while let _ = _ { .. }` (which was desugared to a
|
||||||
|
@ -1740,7 +1742,7 @@ impl MatchSource {
|
||||||
use MatchSource::*;
|
use MatchSource::*;
|
||||||
match self {
|
match self {
|
||||||
Normal => "match",
|
Normal => "match",
|
||||||
IfDesugar { .. } | IfLetDesugar { .. } => "if",
|
IfDesugar { .. } | IfLetDesugar { .. } | IfLetGuardDesugar => "if",
|
||||||
WhileDesugar | WhileLetDesugar => "while",
|
WhileDesugar | WhileLetDesugar => "while",
|
||||||
ForLoopDesugar => "for",
|
ForLoopDesugar => "for",
|
||||||
TryDesugar => "?",
|
TryDesugar => "?",
|
||||||
|
|
|
@ -228,6 +228,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
guard: Option<&Guard<'tcx>>,
|
guard: Option<&Guard<'tcx>>,
|
||||||
fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
|
fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
|
||||||
scrutinee_span: Span,
|
scrutinee_span: Span,
|
||||||
|
arm_span: Option<Span>,
|
||||||
arm_scope: Option<region::Scope>,
|
arm_scope: Option<region::Scope>,
|
||||||
) -> BasicBlock {
|
) -> BasicBlock {
|
||||||
if candidate.subcandidates.is_empty() {
|
if candidate.subcandidates.is_empty() {
|
||||||
|
@ -239,6 +240,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
guard,
|
guard,
|
||||||
fake_borrow_temps,
|
fake_borrow_temps,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
|
arm_span,
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,6 +276,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
guard,
|
guard,
|
||||||
&fake_borrow_temps,
|
&fake_borrow_temps,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
|
arm_span,
|
||||||
schedule_drops,
|
schedule_drops,
|
||||||
);
|
);
|
||||||
if arm_scope.is_none() {
|
if arm_scope.is_none() {
|
||||||
|
@ -436,6 +439,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
&fake_borrow_temps,
|
&fake_borrow_temps,
|
||||||
irrefutable_pat.span,
|
irrefutable_pat.span,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
.unit()
|
.unit()
|
||||||
}
|
}
|
||||||
|
@ -817,11 +821,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// For an example of a case where we set `otherwise_block`, even for an
|
/// For an example of a case where we set `otherwise_block`, even for an
|
||||||
/// exhaustive match consider:
|
/// exhaustive match consider:
|
||||||
///
|
///
|
||||||
|
/// ```rust
|
||||||
/// match x {
|
/// match x {
|
||||||
/// (true, true) => (),
|
/// (true, true) => (),
|
||||||
/// (_, false) => (),
|
/// (_, false) => (),
|
||||||
/// (false, true) => (),
|
/// (false, true) => (),
|
||||||
/// }
|
/// }
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// For this match, we check if `x.0` matches `true` (for the first
|
/// For this match, we check if `x.0` matches `true` (for the first
|
||||||
/// arm). If that's false, we check `x.1`. If it's `true` we check if
|
/// arm). If that's false, we check `x.1`. If it's `true` we check if
|
||||||
|
@ -935,11 +941,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// Link up matched candidates. For example, if we have something like
|
/// Link up matched candidates. For example, if we have something like
|
||||||
/// this:
|
/// this:
|
||||||
///
|
///
|
||||||
|
/// ```rust
|
||||||
/// ...
|
/// ...
|
||||||
/// Some(x) if cond => ...
|
/// Some(x) if cond => ...
|
||||||
/// Some(x) => ...
|
/// Some(x) => ...
|
||||||
/// Some(x) if cond => ...
|
/// Some(x) if cond => ...
|
||||||
/// ...
|
/// ...
|
||||||
|
/// ```
|
||||||
///
|
///
|
||||||
/// We generate real edges from:
|
/// We generate real edges from:
|
||||||
/// * `start_block` to the `prebinding_block` of the first pattern,
|
/// * `start_block` to the `prebinding_block` of the first pattern,
|
||||||
|
@ -1517,7 +1525,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// Initializes each of the bindings from the candidate by
|
/// Initializes each of the bindings from the candidate by
|
||||||
/// moving/copying/ref'ing the source as appropriate. Tests the guard, if
|
/// moving/copying/ref'ing the source as appropriate. Tests the guard, if
|
||||||
/// any, and then branches to the arm. Returns the block for the case where
|
/// any, and then branches to the arm. Returns the block for the case where
|
||||||
/// the guard fails.
|
/// the guard succeeds.
|
||||||
///
|
///
|
||||||
/// Note: we do not check earlier that if there is a guard,
|
/// Note: we do not check earlier that if there is a guard,
|
||||||
/// there cannot be move bindings. We avoid a use-after-move by only
|
/// there cannot be move bindings. We avoid a use-after-move by only
|
||||||
|
@ -1529,6 +1537,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
guard: Option<&Guard<'tcx>>,
|
guard: Option<&Guard<'tcx>>,
|
||||||
fake_borrows: &Vec<(Place<'tcx>, Local)>,
|
fake_borrows: &Vec<(Place<'tcx>, Local)>,
|
||||||
scrutinee_span: Span,
|
scrutinee_span: Span,
|
||||||
|
arm_span: Option<Span>,
|
||||||
schedule_drops: bool,
|
schedule_drops: bool,
|
||||||
) -> BasicBlock {
|
) -> BasicBlock {
|
||||||
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
|
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
|
||||||
|
@ -1659,15 +1668,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
|
self.cfg.push_assign(block, scrutinee_source_info, Place::from(temp), borrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the block to branch to if the guard fails; if there is no
|
let (guard_span, (post_guard_block, otherwise_post_guard_block)) = match guard {
|
||||||
// guard, this block is simply unreachable
|
Guard::If(e) => {
|
||||||
let guard = match guard {
|
let e = self.hir.mirror(e.clone());
|
||||||
Guard::If(e) => self.hir.mirror(e.clone()),
|
let source_info = self.source_info(e.span);
|
||||||
|
(e.span, self.test_bool(block, e, source_info))
|
||||||
|
},
|
||||||
|
Guard::IfLet(pat, scrutinee) => {
|
||||||
|
let scrutinee_span = scrutinee.span();
|
||||||
|
let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee.clone(), scrutinee_span));
|
||||||
|
let mut guard_candidate = Candidate::new(scrutinee_place, &pat, false);
|
||||||
|
let wildcard = Pat::wildcard_from_ty(pat.ty);
|
||||||
|
let mut otherwise_candidate = Candidate::new(scrutinee_place, &wildcard, false);
|
||||||
|
let fake_borrow_temps =
|
||||||
|
self.lower_match_tree(block, pat.span, false, &mut [&mut guard_candidate, &mut otherwise_candidate]);
|
||||||
|
self.declare_bindings(
|
||||||
|
None,
|
||||||
|
pat.span.to(arm_span.unwrap()),
|
||||||
|
pat,
|
||||||
|
ArmHasGuard(false),
|
||||||
|
Some((Some(&scrutinee_place), scrutinee.span())),
|
||||||
|
);
|
||||||
|
let post_guard_block = self.bind_pattern(
|
||||||
|
self.source_info(pat.span),
|
||||||
|
guard_candidate,
|
||||||
|
None,
|
||||||
|
&fake_borrow_temps,
|
||||||
|
scrutinee.span(),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
||||||
|
(scrutinee_span, (post_guard_block, otherwise_post_guard_block))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let source_info = self.source_info(guard.span);
|
let source_info = self.source_info(guard_span);
|
||||||
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard.span));
|
let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
|
||||||
let (post_guard_block, otherwise_post_guard_block) =
|
|
||||||
self.test_bool(block, guard, source_info);
|
|
||||||
let guard_frame = self.guard_context.pop().unwrap();
|
let guard_frame = self.guard_context.pop().unwrap();
|
||||||
debug!("Exiting guard building context with locals: {:?}", guard_frame);
|
debug!("Exiting guard building context with locals: {:?}", guard_frame);
|
||||||
|
|
||||||
|
|
|
@ -1220,6 +1220,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
arm.guard.as_ref(),
|
arm.guard.as_ref(),
|
||||||
&fake_borrow_temps,
|
&fake_borrow_temps,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
|
Some(arm.span),
|
||||||
Some(arm.scope),
|
Some(arm.scope),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -778,7 +778,7 @@ fn convert_arm<'tcx>(cx: &mut Cx<'_, 'tcx>, arm: &'tcx hir::Arm<'tcx>) -> Arm<'t
|
||||||
pattern: cx.pattern_from_hir(&arm.pat),
|
pattern: cx.pattern_from_hir(&arm.pat),
|
||||||
guard: arm.guard.as_ref().map(|g| match g {
|
guard: arm.guard.as_ref().map(|g| match g {
|
||||||
hir::Guard::If(ref e) => Guard::If(e.to_ref()),
|
hir::Guard::If(ref e) => Guard::If(e.to_ref()),
|
||||||
hir::Guard::IfLet(ref pat, ref e) => Guard::IfLet(cx.pattern_from_hir(pat), e.to_ref())
|
hir::Guard::IfLet(ref pat, ref e) => Guard::IfLet(cx.pattern_from_hir(pat), e.to_ref()),
|
||||||
}),
|
}),
|
||||||
body: arm.body.to_ref(),
|
body: arm.body.to_ref(),
|
||||||
lint_level: LintLevel::Explicit(arm.hir_id),
|
lint_level: LintLevel::Explicit(arm.hir_id),
|
||||||
|
|
|
@ -164,10 +164,20 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
// Check the arm for some things unrelated to exhaustiveness.
|
// Check the arm for some things unrelated to exhaustiveness.
|
||||||
self.check_patterns(&arm.pat);
|
self.check_patterns(&arm.pat);
|
||||||
|
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
||||||
|
self.check_patterns(pat);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cx = self.new_cx(scrut.hir_id);
|
let mut cx = self.new_cx(scrut.hir_id);
|
||||||
|
|
||||||
|
for arm in arms {
|
||||||
|
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
||||||
|
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
||||||
|
check_if_let_guard(&mut cx, &tpat, pat.hir_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut have_errors = false;
|
let mut have_errors = false;
|
||||||
|
|
||||||
let arms: Vec<_> = arms
|
let arms: Vec<_> = arms
|
||||||
|
@ -360,12 +370,28 @@ fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::
|
||||||
let msg = match source {
|
let msg = match source {
|
||||||
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
|
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
|
||||||
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
|
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
|
||||||
|
hir::MatchSource::IfLetGuardDesugar => "irrefutable if-let guard",
|
||||||
_ => bug!(),
|
_ => bug!(),
|
||||||
};
|
};
|
||||||
lint.build(msg).emit()
|
lint.build(msg).emit()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_if_let_guard<'p, 'tcx>(
|
||||||
|
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||||
|
pat: &'p super::Pat<'tcx>,
|
||||||
|
pat_id: HirId,
|
||||||
|
) {
|
||||||
|
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
|
||||||
|
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
|
||||||
|
report_arm_reachability(&cx, &report, hir::MatchSource::IfLetGuardDesugar);
|
||||||
|
|
||||||
|
if report.non_exhaustiveness_witnesses.is_empty() {
|
||||||
|
// The match is exhaustive, i.e. the if let pattern is irrefutable.
|
||||||
|
irrefutable_let_pattern(cx.tcx, pat.span, pat_id, hir::MatchSource::IfLetGuardDesugar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Report unreachable arms, if any.
|
/// Report unreachable arms, if any.
|
||||||
fn report_arm_reachability<'p, 'tcx>(
|
fn report_arm_reachability<'p, 'tcx>(
|
||||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||||
|
@ -390,6 +416,11 @@ fn report_arm_reachability<'p, 'tcx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hir::MatchSource::IfLetGuardDesugar => {
|
||||||
|
assert_eq!(arm_index, 0);
|
||||||
|
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None);
|
||||||
|
}
|
||||||
|
|
||||||
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
|
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
|
||||||
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
|
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,8 @@ impl NonConstExpr {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::Match(IfLetGuardDesugar) => bug!("if-let guard outside a `match` expression"),
|
||||||
|
|
||||||
// All other expressions are allowed.
|
// All other expressions are allowed.
|
||||||
Self::Loop(Loop | While | WhileLet)
|
Self::Loop(Loop | While | WhileLet)
|
||||||
| Self::Match(
|
| Self::Match(
|
||||||
|
|
|
@ -730,6 +730,7 @@ fn desugaring_name(des: hir::MatchSource) -> String {
|
||||||
"MatchSource::IfLetDesugar {{ contains_else_clause: {} }}",
|
"MatchSource::IfLetDesugar {{ contains_else_clause: {} }}",
|
||||||
contains_else_clause
|
contains_else_clause
|
||||||
),
|
),
|
||||||
|
hir::MatchSource::IfLetGuardDesugar => "MatchSource::IfLetGuardDesugar".to_string(),
|
||||||
hir::MatchSource::IfDesugar { contains_else_clause } => format!(
|
hir::MatchSource::IfDesugar { contains_else_clause } => format!(
|
||||||
"MatchSource::IfDesugar {{ contains_else_clause: {} }}",
|
"MatchSource::IfDesugar {{ contains_else_clause: {} }}",
|
||||||
contains_else_clause
|
contains_else_clause
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue