guard: Option<&Guard<'tcx>>,
fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
scrutinee_span: Span,
+ arm_span: Option<Span>,
arm_scope: Option<region::Scope>,
) -> BasicBlock {
if candidate.subcandidates.is_empty() {
guard,
fake_borrow_temps,
scrutinee_span,
+ arm_span,
true,
)
} else {
guard,
&fake_borrow_temps,
scrutinee_span,
+ arm_span,
schedule_drops,
);
if arm_scope.is_none() {
&fake_borrow_temps,
irrefutable_pat.span,
None,
+ None,
)
.unit()
}
/// For an example of a case where we set `otherwise_block`, even for an
/// exhaustive match consider:
///
+ /// ```rust
/// match x {
/// (true, true) => (),
/// (_, false) => (),
/// (false, true) => (),
/// }
+ /// ```
///
/// 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
/// Link up matched candidates. For example, if we have something like
/// this:
///
+ /// ```rust
/// ...
/// Some(x) if cond => ...
/// Some(x) => ...
/// Some(x) if cond => ...
/// ...
+ /// ```
///
/// We generate real edges from:
/// * `start_block` to the `prebinding_block` of the first pattern,
/// Initializes each of the bindings from the candidate by
/// 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
- /// the guard fails.
+ /// the guard succeeds.
///
/// 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
guard: Option<&Guard<'tcx>>,
fake_borrows: &Vec<(Place<'tcx>, Local)>,
scrutinee_span: Span,
+ arm_span: Option<Span>,
schedule_drops: bool,
) -> BasicBlock {
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
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
- // guard, this block is simply unreachable
- let guard = match guard {
- Guard::If(e) => self.hir.mirror(e.clone()),
+ let (guard_span, (post_guard_block, otherwise_post_guard_block)) = match guard {
+ Guard::If(e) => {
+ let 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 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 source_info = self.source_info(guard_span);
+ let guard_end = self.source_info(tcx.sess.source_map().end_point(guard_span));
let guard_frame = self.guard_context.pop().unwrap();
debug!("Exiting guard building context with locals: {:?}", guard_frame);
for arm in arms {
// Check the arm for some things unrelated to exhaustiveness.
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);
+ 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 arms: Vec<_> = arms
let msg = match source {
hir::MatchSource::IfLetDesugar { .. } => "irrefutable if-let pattern",
hir::MatchSource::WhileLetDesugar => "irrefutable while-let pattern",
+ hir::MatchSource::IfLetGuardDesugar => "irrefutable if-let guard",
_ => bug!(),
};
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.
fn report_arm_reachability<'p, 'tcx>(
cx: &MatchCheckCtxt<'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 => {
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
}