Auto merge of #53815 - F001:if-let-guard, r=petrochenkov

refactor match guard

This is the first step to implement RFC 2294: if-let-guard. Tracking issue: https://github.com/rust-lang/rust/issues/51114

The second step should be introducing another variant `IfLet` in the Guard enum. I separated them into 2 PRs for the convenience of reviewers.

r? @petrochenkov
This commit is contained in:
bors 2018-09-01 20:31:29 +00:00
commit 28bcffead7
22 changed files with 115 additions and 35 deletions

View File

@ -488,8 +488,9 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> {
// expression to target // expression to target
let guard_start = self.add_dummy_node(&[pat_exit]); let guard_start = self.add_dummy_node(&[pat_exit]);
// Visit the guard expression // Visit the guard expression
let guard_exit = self.expr(&guard, guard_start); let guard_exit = match guard {
hir::Guard::If(ref e) => self.expr(e, guard_start),
};
// #47295: We used to have very special case code // #47295: We used to have very special case code
// here for when a pair of arms are both formed // here for when a pair of arms are both formed
// solely from constants, and if so, not add these // solely from constants, and if so, not add these

View File

@ -1102,7 +1102,11 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) { pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm) {
walk_list!(visitor, visit_pat, &arm.pats); walk_list!(visitor, visit_pat, &arm.pats);
walk_list!(visitor, visit_expr, &arm.guard); if let Some(ref g) = arm.guard {
match g {
Guard::If(ref e) => visitor.visit_expr(e),
}
}
visitor.visit_expr(&arm.body); visitor.visit_expr(&arm.body);
walk_list!(visitor, visit_attribute, &arm.attrs); walk_list!(visitor, visit_attribute, &arm.attrs);
} }

View File

@ -1054,7 +1054,10 @@ impl<'a> LoweringContext<'a> {
hir::Arm { hir::Arm {
attrs: self.lower_attrs(&arm.attrs), attrs: self.lower_attrs(&arm.attrs),
pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(), pats: arm.pats.iter().map(|x| self.lower_pat(x)).collect(),
guard: arm.guard.as_ref().map(|ref x| P(self.lower_expr(x))), guard: match arm.guard {
Some(Guard::If(ref x)) => Some(hir::Guard::If(P(self.lower_expr(x)))),
_ => None,
},
body: P(self.lower_expr(&arm.body)), body: P(self.lower_expr(&arm.body)),
} }
} }

View File

@ -1204,10 +1204,15 @@ impl DeclKind {
pub struct Arm { pub struct Arm {
pub attrs: HirVec<Attribute>, pub attrs: HirVec<Attribute>,
pub pats: HirVec<P<Pat>>, pub pats: HirVec<P<Pat>>,
pub guard: Option<P<Expr>>, pub guard: Option<Guard>,
pub body: P<Expr>, pub body: P<Expr>,
} }
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum Guard {
If(P<Expr>),
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Field { pub struct Field {
pub id: NodeId, pub id: NodeId,

View File

@ -1949,11 +1949,15 @@ impl<'a> State<'a> {
self.print_pat(&p)?; self.print_pat(&p)?;
} }
self.s.space()?; self.s.space()?;
if let Some(ref e) = arm.guard { if let Some(ref g) = arm.guard {
match g {
hir::Guard::If(e) => {
self.word_space("if")?; self.word_space("if")?;
self.print_expr(&e)?; self.print_expr(&e)?;
self.s.space()?; self.s.space()?;
} }
}
}
self.word_space("=>")?; self.word_space("=>")?;
match arm.body.node { match arm.body.node {

View File

@ -493,6 +493,10 @@ impl_stable_hash_for!(struct hir::Arm {
body body
}); });
impl_stable_hash_for!(enum hir::Guard {
If(expr),
});
impl_stable_hash_for!(struct hir::Field { impl_stable_hash_for!(struct hir::Field {
id -> _, id -> _,
ident, ident,

View File

@ -792,7 +792,9 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
} }
if let Some(ref guard) = arm.guard { if let Some(ref guard) = arm.guard {
self.consume_expr(&guard); match guard {
hir::Guard::If(ref e) => self.consume_expr(e),
}
} }
self.consume_expr(&arm.body); self.consume_expr(&arm.body);

View File

@ -1030,7 +1030,12 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
let body_succ = let body_succ =
self.propagate_through_expr(&arm.body, succ); self.propagate_through_expr(&arm.body, succ);
let guard_succ = let guard_succ =
self.propagate_through_opt_expr(arm.guard.as_ref().map(|e| &**e), body_succ); self.propagate_through_opt_expr(
arm.guard.as_ref().map(|g|
match g {
hir::Guard::If(e) => &**e,
}),
body_succ);
// only consider the first pattern; any later patterns must have // only consider the first pattern; any later patterns must have
// the same bindings, and we also consider the first pattern to be // the same bindings, and we also consider the first pattern to be
// the "authoritative" set of ids // the "authoritative" set of ids

View File

@ -885,8 +885,10 @@ fn resolve_block<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, blk:
fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &'tcx hir::Arm) { fn resolve_arm<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, arm: &'tcx hir::Arm) {
visitor.terminating_scopes.insert(arm.body.hir_id.local_id); visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
if let Some(ref expr) = arm.guard { if let Some(ref g) = arm.guard {
visitor.terminating_scopes.insert(expr.hir_id.local_id); match g {
hir::Guard::If(ref expr) => visitor.terminating_scopes.insert(expr.hir_id.local_id),
};
} }
intravisit::walk_arm(visitor, arm); intravisit::walk_arm(visitor, arm);

View File

@ -453,7 +453,7 @@ pub struct Candidate<'pat, 'tcx:'pat> {
bindings: Vec<Binding<'tcx>>, bindings: Vec<Binding<'tcx>>,
// ...and the guard must be evaluated... // ...and the guard must be evaluated...
guard: Option<ExprRef<'tcx>>, guard: Option<Guard<'tcx>>,
// ...and then we branch to arm with this index. // ...and then we branch to arm with this index.
arm_index: usize, arm_index: usize,
@ -998,7 +998,9 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
// the block to branch to if the guard fails; if there is no // the block to branch to if the guard fails; if there is no
// guard, this block is simply unreachable // guard, this block is simply unreachable
let guard = self.hir.mirror(guard); let guard = match guard {
Guard::If(e) => self.hir.mirror(e),
};
let source_info = self.source_info(guard.span); let source_info = self.source_info(guard.span);
let cond = unpack!(block = self.as_local_operand(block, guard)); let cond = unpack!(block = self.as_local_operand(block, guard));
if autoref { if autoref {

View File

@ -837,7 +837,10 @@ impl ToBorrowKind for hir::Mutability {
fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> { fn convert_arm<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>, arm: &'tcx hir::Arm) -> Arm<'tcx> {
Arm { Arm {
patterns: arm.pats.iter().map(|p| cx.pattern_from_hir(p)).collect(), patterns: arm.pats.iter().map(|p| cx.pattern_from_hir(p)).collect(),
guard: arm.guard.to_ref(), guard: match arm.guard {
Some(hir::Guard::If(ref e)) => Some(Guard::If(e.to_ref())),
_ => None,
},
body: arm.body.to_ref(), body: arm.body.to_ref(),
// BUG: fix this // BUG: fix this
lint_level: LintLevel::Inherited, lint_level: LintLevel::Inherited,

View File

@ -316,11 +316,16 @@ pub struct FruInfo<'tcx> {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Arm<'tcx> { pub struct Arm<'tcx> {
pub patterns: Vec<Pattern<'tcx>>, pub patterns: Vec<Pattern<'tcx>>,
pub guard: Option<ExprRef<'tcx>>, pub guard: Option<Guard<'tcx>>,
pub body: ExprRef<'tcx>, pub body: ExprRef<'tcx>,
pub lint_level: LintLevel, pub lint_level: LintLevel,
} }
#[derive(Clone, Debug)]
pub enum Guard<'tcx> {
If(ExprRef<'tcx>),
}
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum LogicalOp { pub enum LogicalOp {
And, And,

View File

@ -208,7 +208,9 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
} }
(pattern, &**pat) (pattern, &**pat)
}).collect(), }).collect(),
arm.guard.as_ref().map(|e| &**e) arm.guard.as_ref().map(|g| match g {
hir::Guard::If(ref e) => &**e,
})
)).collect(); )).collect();
// Bail out early if inlining failed. // Bail out early if inlining failed.
@ -575,12 +577,19 @@ fn check_legality_of_move_bindings(cx: &MatchVisitor,
/// assign. /// assign.
/// ///
/// FIXME: this should be done by borrowck. /// FIXME: this should be done by borrowck.
fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) { fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Guard) {
let mut checker = MutationChecker { let mut checker = MutationChecker {
cx, cx,
}; };
ExprUseVisitor::new(&mut checker, cx.tcx, cx.param_env, cx.region_scope_tree, cx.tables, None) match guard {
.walk_expr(guard); hir::Guard::If(expr) =>
ExprUseVisitor::new(&mut checker,
cx.tcx,
cx.param_env,
cx.region_scope_tree,
cx.tables,
None).walk_expr(expr),
};
} }
struct MutationChecker<'a, 'tcx: 'a> { struct MutationChecker<'a, 'tcx: 'a> {

View File

@ -577,7 +577,7 @@ fn check_expr_kind<'a, 'tcx>(
for index in hirvec_arm.iter() { for index in hirvec_arm.iter() {
let _ = v.check_expr(&*index.body); let _ = v.check_expr(&*index.body);
match index.guard { match index.guard {
Some(ref expr) => { Some(hir::Guard::If(ref expr)) => {
let _ = v.check_expr(&expr); let _ = v.check_expr(&expr);
}, },
None => {}, None => {},

View File

@ -2701,7 +2701,10 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
// This has to happen *after* we determine which pat_idents are variants // This has to happen *after* we determine which pat_idents are variants
self.check_consistent_bindings(&arm.pats); self.check_consistent_bindings(&arm.pats);
walk_list!(self, visit_expr, &arm.guard); match arm.guard {
Some(ast::Guard::If(ref expr)) => self.visit_expr(expr),
_ => {}
}
self.visit_expr(&arm.body); self.visit_expr(&arm.body);
self.ribs[ValueNS].pop(); self.ribs[ValueNS].pop();

View File

@ -1663,7 +1663,10 @@ impl<'l, 'tcx: 'l, 'll, O: DumpOutput + 'll> Visitor<'l> for DumpVisitor<'l, 'tc
fn visit_arm(&mut self, arm: &'l ast::Arm) { fn visit_arm(&mut self, arm: &'l ast::Arm) {
self.process_var_decl_multi(&arm.pats); self.process_var_decl_multi(&arm.pats);
walk_list!(self, visit_expr, &arm.guard); match arm.guard {
Some(ast::Guard::If(ref expr)) => self.visit_expr(expr),
_ => {}
}
self.visit_expr(&arm.body); self.visit_expr(&arm.body);
} }

View File

@ -663,9 +663,11 @@ https://doc.rust-lang.org/reference/types.html#trait-objects");
}; };
for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() { for (i, (arm, pats_diverge)) in arms.iter().zip(all_arm_pats_diverge).enumerate() {
if let Some(ref e) = arm.guard { if let Some(ref g) = arm.guard {
self.diverges.set(pats_diverge); self.diverges.set(pats_diverge);
self.check_expr_has_type_or_error(e, tcx.types.bool); match g {
hir::Guard::If(e) => self.check_expr_has_type_or_error(e, tcx.types.bool),
};
} }
self.diverges.set(pats_diverge); self.diverges.set(pats_diverge);

View File

@ -857,10 +857,15 @@ pub struct Local {
pub struct Arm { pub struct Arm {
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub pats: Vec<P<Pat>>, pub pats: Vec<P<Pat>>,
pub guard: Option<P<Expr>>, pub guard: Option<Guard>,
pub body: P<Expr>, pub body: P<Expr>,
} }
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub enum Guard {
If(P<Expr>),
}
#[derive(Clone, RustcEncodable, RustcDecodable, Debug)] #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
pub struct Field { pub struct Field {
pub ident: Ident, pub ident: Ident,

View File

@ -118,6 +118,10 @@ pub trait Folder : Sized {
noop_fold_arm(a, self) noop_fold_arm(a, self)
} }
fn fold_guard(&mut self, g: Guard) -> Guard {
noop_fold_guard(g, self)
}
fn fold_pat(&mut self, p: P<Pat>) -> P<Pat> { fn fold_pat(&mut self, p: P<Pat>) -> P<Pat> {
noop_fold_pat(p, self) noop_fold_pat(p, self)
} }
@ -354,11 +358,17 @@ pub fn noop_fold_arm<T: Folder>(Arm {attrs, pats, guard, body}: Arm,
Arm { Arm {
attrs: fold_attrs(attrs, fld), attrs: fold_attrs(attrs, fld),
pats: pats.move_map(|x| fld.fold_pat(x)), pats: pats.move_map(|x| fld.fold_pat(x)),
guard: guard.map(|x| fld.fold_expr(x)), guard: guard.map(|x| fld.fold_guard(x)),
body: fld.fold_expr(body), body: fld.fold_expr(body),
} }
} }
pub fn noop_fold_guard<T: Folder>(g: Guard, fld: &mut T) -> Guard {
match g {
Guard::If(e) => Guard::If(fld.fold_expr(e)),
}
}
pub fn noop_fold_ty_binding<T: Folder>(b: TypeBinding, fld: &mut T) -> TypeBinding { pub fn noop_fold_ty_binding<T: Folder>(b: TypeBinding, fld: &mut T) -> TypeBinding {
TypeBinding { TypeBinding {
id: fld.new_id(b.id), id: fld.new_id(b.id),

View File

@ -12,7 +12,7 @@ use rustc_target::spec::abi::{self, Abi};
use ast::{AngleBracketedArgs, ParenthesisedArgs, AttrStyle, BareFnTy}; use ast::{AngleBracketedArgs, ParenthesisedArgs, AttrStyle, BareFnTy};
use ast::{GenericBound, TraitBoundModifier}; use ast::{GenericBound, TraitBoundModifier};
use ast::Unsafety; use ast::Unsafety;
use ast::{Mod, AnonConst, Arg, Arm, Attribute, BindingMode, TraitItemKind}; use ast::{Mod, AnonConst, Arg, Arm, Guard, Attribute, BindingMode, TraitItemKind};
use ast::Block; use ast::Block;
use ast::{BlockCheckMode, CaptureBy, Movability}; use ast::{BlockCheckMode, CaptureBy, Movability};
use ast::{Constness, Crate}; use ast::{Constness, Crate};
@ -3533,7 +3533,7 @@ impl<'a> Parser<'a> {
self.eat(&token::BinOp(token::Or)); self.eat(&token::BinOp(token::Or));
let pats = self.parse_pats()?; let pats = self.parse_pats()?;
let guard = if self.eat_keyword(keywords::If) { let guard = if self.eat_keyword(keywords::If) {
Some(self.parse_expr()?) Some(Guard::If(self.parse_expr()?))
} else { } else {
None None
}; };

View File

@ -2702,11 +2702,15 @@ impl<'a> State<'a> {
self.print_outer_attributes(&arm.attrs)?; self.print_outer_attributes(&arm.attrs)?;
self.print_pats(&arm.pats)?; self.print_pats(&arm.pats)?;
self.s.space()?; self.s.space()?;
if let Some(ref e) = arm.guard { if let Some(ref g) = arm.guard {
match g {
ast::Guard::If(ref e) => {
self.word_space("if")?; self.word_space("if")?;
self.print_expr(e)?; self.print_expr(e)?;
self.s.space()?; self.s.space()?;
} }
}
}
self.word_space("=>")?; self.word_space("=>")?;
match arm.body.node { match arm.body.node {

View File

@ -819,7 +819,11 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) { pub fn walk_arm<'a, V: Visitor<'a>>(visitor: &mut V, arm: &'a Arm) {
walk_list!(visitor, visit_pat, &arm.pats); walk_list!(visitor, visit_pat, &arm.pats);
walk_list!(visitor, visit_expr, &arm.guard); if let Some(ref g) = &arm.guard {
match g {
Guard::If(ref e) => visitor.visit_expr(e),
}
}
visitor.visit_expr(&arm.body); visitor.visit_expr(&arm.body);
walk_list!(visitor, visit_attribute, &arm.attrs); walk_list!(visitor, visit_attribute, &arm.attrs);
} }