Implement general or-patterns in match expressions

This commit is contained in:
Matthew Jasper 2019-12-27 13:39:43 +00:00
parent 64eab7750b
commit 5cc4352bc4
4 changed files with 381 additions and 88 deletions

View File

@ -26,7 +26,9 @@ mod simplify;
mod test; mod test;
mod util; mod util;
use std::borrow::Borrow;
use std::convert::TryFrom; use std::convert::TryFrom;
use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> { impl<'a, 'tcx> Builder<'a, 'tcx> {
/// Generates MIR for a `match` expression. /// Generates MIR for a `match` expression.
@ -95,7 +97,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); let match_has_guard = arms.iter().any(|arm| arm.guard.is_some());
let candidates = let candidates =
arm_candidates.iter_mut().flat_map(|(_, candidates)| candidates).collect::<Vec<_>>(); arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
let fake_borrow_temps = let fake_borrow_temps =
self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates); self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates);
@ -145,27 +147,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self, &mut self,
scrutinee: &Place<'tcx>, scrutinee: &Place<'tcx>,
arms: &'pat [Arm<'tcx>], arms: &'pat [Arm<'tcx>],
) -> Vec<(&'pat Arm<'tcx>, Vec<Candidate<'pat, 'tcx>>)> { ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> {
// Assemble a list of candidates: there is one candidate per pattern, // Assemble a list of candidates: there is one candidate per pattern,
// which means there may be more than one candidate *per arm*. // which means there may be more than one candidate *per arm*.
arms.iter() arms.iter()
.map(|arm| { .map(|arm| {
let arm_has_guard = arm.guard.is_some(); let arm_has_guard = arm.guard.is_some();
let arm_candidates: Vec<_> = arm let arm_candidate = Candidate {
.top_pats_hack() span: arm.pattern.span,
.iter() match_pairs: smallvec![MatchPair::new(*scrutinee, &arm.pattern),],
.map(|pattern| Candidate {
span: pattern.span,
has_guard: arm_has_guard,
match_pairs: smallvec![MatchPair::new(*scrutinee, pattern)],
bindings: vec![], bindings: vec![],
ascriptions: vec![], ascriptions: vec![],
has_guard: arm_has_guard,
needs_otherwise_block: arm_has_guard,
otherwise_block: None, otherwise_block: None,
pre_binding_block: None, pre_binding_block: None,
next_candidate_pre_binding_block: None, next_candidate_pre_binding_block: None,
}) subcandidates: vec![],
.collect(); };
(arm, arm_candidates) (arm, arm_candidate)
}) })
.collect() .collect()
} }
@ -205,11 +205,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
} }
let mut next_prebinding_block = None; let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
for candidate in candidates.iter_mut().rev() { for candidate in candidates.into_iter() {
candidate.next_candidate_pre_binding_block = next_prebinding_block; candidate.visit_leaves(|leaf_candidate| {
next_prebinding_block = candidate.pre_binding_block; if let Some(ref mut prev) = previous_candidate {
prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block;
}
previous_candidate = Some(leaf_candidate);
});
} }
if let Some(ref borrows) = fake_borrows { if let Some(ref borrows) = fake_borrows {
@ -230,7 +234,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
destination: &Place<'tcx>, destination: &Place<'tcx>,
scrutinee_place: Place<'tcx>, scrutinee_place: Place<'tcx>,
scrutinee_span: Span, scrutinee_span: Span,
arm_candidates: Vec<(&'_ Arm<'tcx>, Vec<Candidate<'_, 'tcx>>)>, arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
outer_source_info: SourceInfo, outer_source_info: SourceInfo,
fake_borrow_temps: Vec<(Place<'tcx>, Local)>, fake_borrow_temps: Vec<(Place<'tcx>, Local)>,
) -> BlockAnd<()> { ) -> BlockAnd<()> {
@ -238,8 +242,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let arm_end_blocks: Vec<_> = arm_candidates let arm_end_blocks: Vec<_> = arm_candidates
.into_iter() .into_iter()
.map(|(arm, candidates)| { .map(|(arm, candidate)| {
debug!("lowering arm {:?}\ncanidates = {:?}", arm, candidates); debug!("lowering arm {:?}\ncanidate = {:?}", arm, candidate);
let arm_source_info = self.source_info(arm.span); let arm_source_info = self.source_info(arm.span);
let arm_scope = (arm.scope, arm_source_info); let arm_scope = (arm.scope, arm_source_info);
@ -248,14 +252,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let scope = this.declare_bindings( let scope = this.declare_bindings(
None, None,
arm.span, arm.span,
&arm.top_pats_hack()[0], &arm.pattern,
ArmHasGuard(arm.guard.is_some()), ArmHasGuard(arm.guard.is_some()),
Some((Some(&scrutinee_place), scrutinee_span)), Some((Some(&scrutinee_place), scrutinee_span)),
); );
let arm_block = this.bind_pattern( let arm_block = this.bind_pattern(
outer_source_info, outer_source_info,
candidates, candidate,
arm.guard.as_ref().map(|g| (g, match_scope)), arm.guard.as_ref().map(|g| (g, match_scope)),
&fake_borrow_temps, &fake_borrow_temps,
scrutinee_span, scrutinee_span,
@ -289,35 +293,52 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_pattern( fn bind_pattern(
&mut self, &mut self,
outer_source_info: SourceInfo, outer_source_info: SourceInfo,
mut candidates: Vec<Candidate<'_, 'tcx>>, candidate: Candidate<'_, 'tcx>,
guard: Option<(&Guard<'tcx>, region::Scope)>, guard: Option<(&Guard<'tcx>, region::Scope)>,
fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>,
scrutinee_span: Span, scrutinee_span: Span,
arm_scope: region::Scope, arm_scope: region::Scope,
) -> BasicBlock { ) -> BasicBlock {
if candidates.len() == 1 { if candidate.subcandidates.is_empty() {
// Avoid generating another `BasicBlock` when we only have one // Avoid generating another `BasicBlock` when we only have one
// candidate. // candidate.
self.bind_and_guard_matched_candidate( self.bind_and_guard_matched_candidate(
candidates.pop().unwrap(), candidate,
&[],
guard, guard,
fake_borrow_temps, fake_borrow_temps,
scrutinee_span, scrutinee_span,
) )
} else { } else {
let arm_block = self.cfg.start_new_block(); let target_block = self.cfg.start_new_block();
for candidate in candidates {
// Avoid scheduling drops multiple times. // We keep a stack of all of the bindings and type asciptions
// from the the parent candidates that we visit, that also need to
// be bound for each candidate.
traverse_candidate(
candidate,
&mut Vec::new(),
&mut |leaf_candidate, parent_bindings| {
self.clear_top_scope(arm_scope); self.clear_top_scope(arm_scope);
let binding_end = self.bind_and_guard_matched_candidate( let binding_end = self.bind_and_guard_matched_candidate(
candidate, leaf_candidate,
parent_bindings,
guard, guard,
fake_borrow_temps, &fake_borrow_temps,
scrutinee_span, scrutinee_span,
); );
self.cfg.goto(binding_end, outer_source_info, arm_block); self.cfg.goto(binding_end, outer_source_info, target_block);
} },
arm_block |inner_candidate, parent_bindings| {
parent_bindings.push((inner_candidate.bindings, inner_candidate.ascriptions));
inner_candidate.subcandidates.into_iter()
},
|parent_bindings| {
parent_bindings.pop();
},
);
target_block
} }
} }
@ -427,6 +448,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mut candidate = Candidate { let mut candidate = Candidate {
span: irrefutable_pat.span, span: irrefutable_pat.span,
has_guard: false, has_guard: false,
needs_otherwise_block: false,
match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)], match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)],
bindings: vec![], bindings: vec![],
ascriptions: vec![], ascriptions: vec![],
@ -435,6 +457,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
otherwise_block: None, otherwise_block: None,
pre_binding_block: None, pre_binding_block: None,
next_candidate_pre_binding_block: None, next_candidate_pre_binding_block: None,
subcandidates: vec![],
}; };
// Simplify the candidate. Since the pattern is irrefutable, this should // Simplify the candidate. Since the pattern is irrefutable, this should
@ -632,9 +655,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
PatKind::Or { ref pats } => { PatKind::Or { ref pats } => {
for pat in pats { self.visit_bindings(&pats[0], pattern_user_ty.clone(), f);
self.visit_bindings(&pat, pattern_user_ty.clone(), f);
}
} }
} }
} }
@ -642,28 +663,72 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
#[derive(Debug)] #[derive(Debug)]
crate struct Candidate<'pat, 'tcx> { crate struct Candidate<'pat, 'tcx> {
// span of the original pattern that gave rise to this candidate /// `Span` of the original pattern that gave rise to this candidate
span: Span, span: Span,
/// This `Candidate` has a guard.
has_guard: bool, has_guard: bool,
// all of these must be satisfied... /// This `Candidate` needs and otherwise block, either because it has a
/// guard or it has subcandidates.
needs_otherwise_block: bool,
/// All of these must be satisfied...
match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
// ...these bindings established... /// ...these bindings established...
bindings: Vec<Binding<'tcx>>, bindings: Vec<Binding<'tcx>>,
// ...and these types asserted... /// ...and these types asserted...
ascriptions: Vec<Ascription<'tcx>>, ascriptions: Vec<Ascription<'tcx>>,
// ...and the guard must be evaluated, if false branch to Block... /// ... and if this is non-empty, one of these subcandidates also has to match ...
subcandidates: Vec<Candidate<'pat, 'tcx>>,
/// ...and the guard must be evaluated, if false branch to Block...
otherwise_block: Option<BasicBlock>, otherwise_block: Option<BasicBlock>,
// ...and the blocks for add false edges between candidates /// ...and the blocks for add false edges between candidates
pre_binding_block: Option<BasicBlock>, pre_binding_block: Option<BasicBlock>,
next_candidate_pre_binding_block: Option<BasicBlock>, next_candidate_pre_binding_block: Option<BasicBlock>,
} }
impl Candidate<'_, '_> {
/// Visit the leaf candidates (those with no subcandidates) contained in
/// this candidate.
fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) {
traverse_candidate(
self,
&mut (),
&mut move |c, _| visit_leaf(c),
move |c, _| c.subcandidates.iter_mut(),
|_| {},
);
}
}
/// A depth-first traversal of the `Candidate` and all of its recursive
/// subcandidates.
fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>(
candidate: C,
context: &mut T,
visit_leaf: &mut impl FnMut(C, &mut T),
get_children: impl Copy + Fn(C, &mut T) -> I,
complete_children: impl Copy + Fn(&mut T),
) where
C: Borrow<Candidate<'pat, 'tcx>>,
I: Iterator<Item = C>,
{
if candidate.borrow().subcandidates.is_empty() {
visit_leaf(candidate, context)
} else {
for child in get_children(candidate, context) {
traverse_candidate(child, context, visit_leaf, get_children, complete_children);
}
complete_children(context)
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Binding<'tcx> { struct Binding<'tcx> {
span: Span, span: Span,
@ -793,10 +858,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Start by simplifying candidates. Once this process is complete, all // Start by simplifying candidates. Once this process is complete, all
// the match pairs which remain require some form of test, whether it // the match pairs which remain require some form of test, whether it
// be a switch or pattern comparison. // be a switch or pattern comparison.
let mut split_or_candidate = false;
for candidate in &mut *candidates { for candidate in &mut *candidates {
self.simplify_candidate(candidate); split_or_candidate |= self.simplify_candidate(candidate);
} }
if split_or_candidate {
// At least one of the candidates has been split into subcandidates.
// We need to change the candidate list to include those.
let mut new_candidates = Vec::new();
for candidate in candidates {
candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
}
self.match_simplified_candidates(
span,
start_block,
otherwise_block,
&mut *new_candidates,
fake_borrows,
);
} else {
self.match_simplified_candidates(
span,
start_block,
otherwise_block,
candidates,
fake_borrows,
);
};
}
fn match_simplified_candidates(
&mut self,
span: Span,
start_block: BasicBlock,
otherwise_block: &mut Option<BasicBlock>,
candidates: &mut [&mut Candidate<_, 'tcx>],
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) {
// The candidates are sorted by priority. Check to see whether the // The candidates are sorted by priority. Check to see whether the
// higher priority candidates (and hence at the front of the slice) // higher priority candidates (and hence at the front of the slice)
// have satisfied all their match pairs. // have satisfied all their match pairs.
@ -835,7 +935,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
// Test for the remaining candidates. // Test for the remaining candidates.
self.test_candidates(span, unmatched_candidates, block, otherwise_block, fake_borrows); self.test_candidates_with_or(
span,
unmatched_candidates,
block,
otherwise_block,
fake_borrows,
);
} }
/// Link up matched candidates. For example, if we have something like /// Link up matched candidates. For example, if we have something like
@ -866,6 +972,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
!matched_candidates.is_empty(), !matched_candidates.is_empty(),
"select_matched_candidates called with no candidates", "select_matched_candidates called with no candidates",
); );
debug_assert!(
matched_candidates.iter().all(|c| c.subcandidates.is_empty()),
"subcandidates should be empty in select_matched_candidates",
);
// Insert a borrows of prefixes of places that are bound and are // Insert a borrows of prefixes of places that are bound and are
// behind a dereference projection. // behind a dereference projection.
@ -902,7 +1012,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let fully_matched_with_guard = matched_candidates let fully_matched_with_guard = matched_candidates
.iter() .iter()
.position(|c| !c.has_guard) .position(|c| !c.needs_otherwise_block)
.unwrap_or(matched_candidates.len() - 1); .unwrap_or(matched_candidates.len() - 1);
let (reachable_candidates, unreachable_candidates) = let (reachable_candidates, unreachable_candidates) =
@ -914,7 +1024,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
assert!(candidate.otherwise_block.is_none()); assert!(candidate.otherwise_block.is_none());
assert!(candidate.pre_binding_block.is_none()); assert!(candidate.pre_binding_block.is_none());
candidate.pre_binding_block = Some(next_prebinding); candidate.pre_binding_block = Some(next_prebinding);
if candidate.has_guard { if candidate.needs_otherwise_block {
next_prebinding = self.cfg.start_new_block(); next_prebinding = self.cfg.start_new_block();
candidate.otherwise_block = Some(next_prebinding); candidate.otherwise_block = Some(next_prebinding);
} }
@ -932,6 +1042,120 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
reachable_candidates.last_mut().unwrap().otherwise_block reachable_candidates.last_mut().unwrap().otherwise_block
} }
fn test_candidates_with_or(
&mut self,
span: Span,
candidates: &mut [&mut Candidate<'_, 'tcx>],
block: BasicBlock,
otherwise_block: &mut Option<BasicBlock>,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) {
let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
if let PatKind::Or { .. } = *first_candidate.match_pairs[0].pattern.kind {
let match_pairs = mem::take(&mut first_candidate.match_pairs);
first_candidate.needs_otherwise_block = true;
first_candidate.pre_binding_block = Some(block);
// We sort or-patterns to the end in `simplify_candidate`, so all
// the remaining match pairs are or-patterns.
for match_pair in match_pairs {
if let PatKind::Or { ref pats } = *match_pair.pattern.kind {
let or_span = match_pair.pattern.span;
let place = &match_pair.place;
first_candidate.visit_leaves(|leaf_candidate| {
self.test_or_pattern(leaf_candidate, pats, or_span, place, fake_borrows);
});
} else {
bug!("Or patterns should have been sorted to the end");
}
}
let remainder_start =
first_candidate.otherwise_block.unwrap_or_else(|| self.cfg.start_new_block());
self.match_candidates(
span,
remainder_start,
otherwise_block,
remaining_candidates,
fake_borrows,
)
} else {
self.test_candidates(span, candidates, block, otherwise_block, fake_borrows)
}
}
fn test_or_pattern<'pat>(
&mut self,
candidate: &mut Candidate<'pat, 'tcx>,
pats: &'pat [Pat<'tcx>],
or_span: Span,
place: &Place<'tcx>,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) {
debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats);
let mut or_candidates: Vec<_> = pats
.iter()
.map(|pat| {
let new_match_pair = smallvec![MatchPair { pattern: pat, place: place.clone() }];
Candidate {
span: pat.span,
has_guard: candidate.has_guard,
needs_otherwise_block: candidate.needs_otherwise_block,
match_pairs: new_match_pair,
bindings: Vec::new(),
ascriptions: Vec::new(),
otherwise_block: None,
pre_binding_block: None,
next_candidate_pre_binding_block: None,
subcandidates: Vec::new(),
}
})
.collect();
let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
self.match_candidates(
or_span,
candidate.pre_binding_block.unwrap(),
&mut candidate.otherwise_block,
&mut or_candidate_refs,
fake_borrows,
);
candidate.subcandidates = or_candidates;
self.merge_trivial_subcandidates(candidate, self.source_info(or_span));
}
/// Try to merge all of the subcandidates of the given candidate into one.
/// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`.
fn merge_trivial_subcandidates(
&mut self,
candidate: &mut Candidate<'_, 'tcx>,
source_info: SourceInfo,
) {
if candidate.subcandidates.is_empty() {
return;
}
let mut can_merge = !candidate.has_guard;
// Not `Iterator::all` because we don't want to short-circuit.
for subcandidate in &mut candidate.subcandidates {
self.merge_trivial_subcandidates(subcandidate, source_info);
// FIXME(or_patterns; matthewjasper) Try to be more aggressive here.
can_merge &= subcandidate.subcandidates.is_empty()
&& subcandidate.bindings.is_empty()
&& subcandidate.ascriptions.is_empty();
}
if can_merge {
let any_matches = self.cfg.start_new_block();
for subcandidate in mem::take(&mut candidate.subcandidates) {
let or_block = subcandidate.pre_binding_block.unwrap();
self.cfg.goto(or_block, source_info, any_matches);
}
candidate.pre_binding_block = Some(any_matches);
}
}
/// This is the most subtle part of the matching algorithm. At /// This is the most subtle part of the matching algorithm. At
/// this point, the input candidates have been fully simplified, /// this point, the input candidates have been fully simplified,
/// and so we know that all remaining match-pairs require some /// and so we know that all remaining match-pairs require some
@ -1258,6 +1482,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_and_guard_matched_candidate<'pat>( fn bind_and_guard_matched_candidate<'pat>(
&mut self, &mut self,
candidate: Candidate<'pat, 'tcx>, candidate: Candidate<'pat, 'tcx>,
parent_bindings: &[(Vec<Binding<'tcx>>, Vec<Ascription<'tcx>>)],
guard: Option<(&Guard<'tcx>, region::Scope)>, guard: Option<(&Guard<'tcx>, region::Scope)>,
fake_borrows: &Vec<(Place<'tcx>, Local)>, fake_borrows: &Vec<(Place<'tcx>, Local)>,
scrutinee_span: Span, scrutinee_span: Span,
@ -1281,7 +1506,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = fresh_block; block = fresh_block;
} }
self.ascribe_types(block, &candidate.ascriptions); self.ascribe_types(
block,
parent_bindings
.iter()
.flat_map(|(_, ascriptions)| ascriptions)
.chain(&candidate.ascriptions),
);
// rust-lang/rust#27282: The `autoref` business deserves some // rust-lang/rust#27282: The `autoref` business deserves some
// explanation here. // explanation here.
@ -1365,14 +1596,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// reference to that. // reference to that.
if let Some((guard, region_scope)) = guard { if let Some((guard, region_scope)) = guard {
let tcx = self.hir.tcx(); let tcx = self.hir.tcx();
let bindings = parent_bindings
self.bind_matched_candidate_for_guard(block, &candidate.bindings);
let guard_frame = GuardFrame {
locals: candidate
.bindings
.iter() .iter()
.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)) .flat_map(|(bindings, _)| bindings)
.collect(), .chain(&candidate.bindings);
self.bind_matched_candidate_for_guard(block, bindings.clone());
let guard_frame = GuardFrame {
locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(),
}; };
debug!("entering guard building context: {:?}", guard_frame); debug!("entering guard building context: {:?}", guard_frame);
self.guard_context.push(guard_frame); self.guard_context.push(guard_frame);
@ -1446,7 +1677,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// ``` // ```
// //
// and that is clearly not correct. // and that is clearly not correct.
let by_value_bindings = candidate.bindings.iter().filter(|binding| { let by_value_bindings =
parent_bindings
.iter()
.flat_map(|(bindings, _)| bindings)
.chain(&candidate.bindings)
.filter(|binding| {
if let BindingMode::ByValue = binding.binding_mode { true } else { false } if let BindingMode::ByValue = binding.binding_mode { true } else { false }
}); });
// Read all of the by reference bindings to ensure that the // Read all of the by reference bindings to ensure that the
@ -1460,18 +1696,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
post_guard_block post_guard_block
} else { } else {
assert!(candidate.otherwise_block.is_none());
// (Here, it is not too early to bind the matched // (Here, it is not too early to bind the matched
// candidate on `block`, because there is no guard result // candidate on `block`, because there is no guard result
// that we have to inspect before we bind them.) // that we have to inspect before we bind them.)
self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); self.bind_matched_candidate_for_arm_body(
block,
parent_bindings
.iter()
.flat_map(|(bindings, _)| bindings)
.chain(&candidate.bindings),
);
block block
} }
} }
/// Append `AscribeUserType` statements onto the end of `block` /// Append `AscribeUserType` statements onto the end of `block`
/// for each ascription /// for each ascription
fn ascribe_types(&mut self, block: BasicBlock, ascriptions: &[Ascription<'tcx>]) { fn ascribe_types<'b>(
&mut self,
block: BasicBlock,
ascriptions: impl IntoIterator<Item = &'b Ascription<'tcx>>,
) where
'tcx: 'b,
{
for ascription in ascriptions { for ascription in ascriptions {
let source_info = self.source_info(ascription.span); let source_info = self.source_info(ascription.span);
@ -1498,14 +1745,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
fn bind_matched_candidate_for_guard(&mut self, block: BasicBlock, bindings: &[Binding<'tcx>]) { fn bind_matched_candidate_for_guard<'b>(
debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})", block, bindings); &mut self,
block: BasicBlock,
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
) where
'tcx: 'b,
{
debug!("bind_matched_candidate_for_guard(block={:?})", block);
// Assign each of the bindings. Since we are binding for a // Assign each of the bindings. Since we are binding for a
// guard expression, this will never trigger moves out of the // guard expression, this will never trigger moves out of the
// candidate. // candidate.
let re_erased = self.hir.tcx().lifetimes.re_erased; let re_erased = self.hir.tcx().lifetimes.re_erased;
for binding in bindings { for binding in bindings {
debug!("bind_matched_candidate_for_guard(binding={:?})", binding);
let source_info = self.source_info(binding.span); let source_info = self.source_info(binding.span);
// For each pattern ident P of type T, `ref_for_guard` is // For each pattern ident P of type T, `ref_for_guard` is

View File

@ -16,18 +16,40 @@ use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
use crate::build::Builder; use crate::build::Builder;
use crate::hair::{self, *}; use crate::hair::{self, *};
use rustc::mir::interpret::truncate; use rustc::mir::interpret::truncate;
use rustc::mir::Place;
use rustc::ty; use rustc::ty;
use rustc::ty::layout::{Integer, IntegerExt, Size}; use rustc::ty::layout::{Integer, IntegerExt, Size};
use rustc_attr::{SignedInt, UnsignedInt}; use rustc_attr::{SignedInt, UnsignedInt};
use rustc_hir::RangeEnd; use rustc_hir::RangeEnd;
use smallvec::smallvec;
use std::mem; use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> { impl<'a, 'tcx> Builder<'a, 'tcx> {
crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) { /// Simplify a candidate so that all match pairs require a test.
///
/// This method will also split a candidate where the only match-pair is an
/// or-pattern into multiple candidates. This is so that
///
/// match x {
/// 0 | 1 => { ... },
/// 2 | 3 => { ... },
/// }
///
/// only generates a single switch. If this happens this method returns
/// `true`.
crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) -> bool {
// repeatedly simplify match pairs until fixed point is reached // repeatedly simplify match pairs until fixed point is reached
loop { loop {
let match_pairs = mem::take(&mut candidate.match_pairs); let match_pairs = mem::take(&mut candidate.match_pairs);
if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, ref place }] =
*match_pairs
{
candidate.subcandidates = self.create_or_subcanidates(candidate, place, pats);
return true;
}
let mut changed = false; let mut changed = false;
for match_pair in match_pairs { for match_pair in match_pairs {
match self.simplify_match_pair(match_pair, candidate) { match self.simplify_match_pair(match_pair, candidate) {
@ -40,11 +62,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
if !changed { if !changed {
return; // if we were not able to simplify any, done. // Move or-patterns to the end, because they can result in us
// creating additional candidates, so we want to test them as
// late as possible.
candidate
.match_pairs
.sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
return false; // if we were not able to simplify any, done.
} }
} }
} }
fn create_or_subcanidates<'pat>(
&mut self,
candidate: &Candidate<'pat, 'tcx>,
place: &Place<'tcx>,
pats: &'pat [Pat<'tcx>],
) -> Vec<Candidate<'pat, 'tcx>> {
pats.iter()
.map(|pat| {
let mut candidate = Candidate {
span: pat.span,
has_guard: candidate.has_guard,
needs_otherwise_block: candidate.needs_otherwise_block,
match_pairs: smallvec![MatchPair { place: place.clone(), pattern: pat }],
bindings: vec![],
ascriptions: vec![],
subcandidates: vec![],
otherwise_block: None,
pre_binding_block: None,
next_candidate_pre_binding_block: None,
};
self.simplify_candidate(&mut candidate);
candidate
})
.collect()
}
/// Tries to simplify `match_pair`, returning `Ok(())` if /// Tries to simplify `match_pair`, returning `Ok(())` if
/// successful. If successful, new match pairs and bindings will /// successful. If successful, new match pairs and bindings will
/// have been pushed into the candidate. If no simplification is /// have been pushed into the candidate. If no simplification is

View File

@ -70,11 +70,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} }
} }
PatKind::Or { .. } => self PatKind::Or { .. } => bug!("or-patterns should have already been handled"),
.hir
.tcx()
.sess
.span_fatal(match_pair.pattern.span, "or-patterns are not fully implemented yet"),
PatKind::AscribeUserType { .. } PatKind::AscribeUserType { .. }
| PatKind::Array { .. } | PatKind::Array { .. }

View File

@ -315,17 +315,6 @@ crate struct Arm<'tcx> {
crate span: Span, crate span: Span,
} }
impl<'tcx> Arm<'tcx> {
// HACK(or_patterns; Centril | dlrobertson): Remove this and
// correctly handle each case in which this method is used.
crate fn top_pats_hack(&self) -> &[Pat<'tcx>] {
match &*self.pattern.kind {
PatKind::Or { pats } => pats,
_ => std::slice::from_ref(&self.pattern),
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
crate enum Guard<'tcx> { crate enum Guard<'tcx> {
If(ExprRef<'tcx>), If(ExprRef<'tcx>),