1
Fork 0

Rollup merge of #79051 - LeSeulArtichaut:if-let-guard, r=matthewjasper

Implement if-let match guards

Implements rust-lang/rfcs#2294 (tracking issue: #51114).

I probably should do a few more things before this can be merged:
- [x] Add tests (added basic tests, more advanced tests could be done in the future?)
- [x] Add lint for exhaustive if-let guard (comparable to normal if-let statements)
- [x] Fix clippy

However since this is a nightly feature maybe it's fine to land this and do those steps in follow-up PRs.

Thanks a lot `@matthewjasper` ❤️ for helping me with lowering to MIR! Would you be interested in reviewing this?
r? `@ghost` for now
This commit is contained in:
Yuki Okushi 2020-12-17 11:43:55 +09:00 committed by GitHub
commit 1e1ba7c936
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 360 additions and 81 deletions

View file

@ -43,7 +43,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// FIXME(60707): Consider removing hack with principled solution.
self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {})
} else {
self.demand_scrutinee_type(arms, scrut)
self.demand_scrutinee_type(scrut, arms_contain_ref_bindings(arms), arms.is_empty())
};
// If there are no arms, that is a diverging match; a special case.
@ -98,7 +98,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.diverges.set(Diverges::Maybe);
match g {
hir::Guard::If(e) => {
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {})
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
}
hir::Guard::IfLet(pat, e) => {
let scrutinee_ty = self.demand_scrutinee_type(
e,
pat.contains_explicit_ref_binding(),
false,
);
self.check_pat_top(&pat, scrutinee_ty, None, true);
}
};
}
@ -450,8 +458,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn demand_scrutinee_type(
&self,
arms: &'tcx [hir::Arm<'tcx>],
scrut: &'tcx hir::Expr<'tcx>,
contains_ref_bindings: Option<hir::Mutability>,
no_arms: bool,
) -> Ty<'tcx> {
// Not entirely obvious: if matches may create ref bindings, we want to
// use the *precise* type of the scrutinee, *not* some supertype, as
@ -505,17 +514,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// (once introduced) is populated by the time we get here.
//
// See #44848.
let contains_ref_bindings = arms
.iter()
.filter_map(|a| a.pat.contains_explicit_ref_binding())
.max_by_key(|m| match *m {
hir::Mutability::Mut => 1,
hir::Mutability::Not => 0,
});
if let Some(m) = contains_ref_bindings {
self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m))
} else if arms.is_empty() {
} else if no_arms {
self.check_expr(scrut)
} else {
// ...but otherwise we want to use any supertype of the
@ -546,3 +547,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
fn arms_contain_ref_bindings(arms: &'tcx [hir::Arm<'tcx>]) -> Option<hir::Mutability> {
arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max_by_key(|m| match *m {
hir::Mutability::Mut => 1,
hir::Mutability::Not => 0,
})
}

View file

@ -246,6 +246,10 @@ impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> {
Guard::If(ref e) => {
self.visit_expr(e);
}
Guard::IfLet(ref pat, ref e) => {
self.visit_pat(pat);
self.visit_expr(e);
}
}
let mut scope_var_ids =