diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index ad55a9fb7b8..1eb0c2cb3e6 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -26,7 +26,6 @@ mod simplify; mod test; mod util; -use itertools::Itertools; use std::convert::TryFrom; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -66,12 +65,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// We generate MIR in the following steps: /// /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]). - /// 2. Create the prebinding and otherwise blocks ([Builder::create_match_candidates]). - /// 3. Create the decision tree ([Builder::lower_match_tree]). - /// 4. Determine the fake borrows that are needed from the places that were + /// 2. Create the decision tree ([Builder::lower_match_tree]). + /// 3. Determine the fake borrows that are needed from the places that were /// matched against and create the required temporaries for them /// ([Builder::calculate_fake_borrows]). - /// 5. Create everything else: the guards and the arms ([Builder::lower_match_arms]). + /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]). /// /// ## False edges /// @@ -148,13 +146,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee: &Place<'tcx>, arms: &'pat [Arm<'tcx>], ) -> Vec<(&'pat Arm<'tcx>, Vec>)> { - let candidate_count = arms.iter().map(|c| c.top_pats_hack().len()).sum::(); - let pre_binding_blocks: Vec<_> = - (0..candidate_count).map(|_| self.cfg.start_new_block()).collect(); - - let mut candidate_pre_binding_blocks = pre_binding_blocks.iter(); - let mut next_candidate_pre_binding_blocks = pre_binding_blocks.iter().skip(1); - // Assemble a list of candidates: there is one candidate per pattern, // which means there may be more than one candidate *per arm*. arms.iter() @@ -163,21 +154,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_candidates: Vec<_> = arm .top_pats_hack() .iter() - .zip(candidate_pre_binding_blocks.by_ref()) - .map(|(pattern, pre_binding_block)| Candidate { + .map(|pattern| Candidate { span: pattern.span, + has_guard: arm_has_guard, match_pairs: smallvec![MatchPair::new(*scrutinee, pattern)], bindings: vec![], ascriptions: vec![], - otherwise_block: if arm_has_guard { - Some(self.cfg.start_new_block()) - } else { - None - }, - pre_binding_block: *pre_binding_block, - next_candidate_pre_binding_block: next_candidate_pre_binding_blocks - .next() - .copied(), + otherwise_block: None, + pre_binding_block: None, + next_candidate_pre_binding_block: None, }) .collect(); (arm, arm_candidates) @@ -203,16 +188,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // them. let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None }; + let mut otherwise = None; + // This will generate code to test scrutinee_place and // branch to the appropriate arm block self.match_candidates( scrutinee_span, - &mut Some(block), - None, + block, + &mut otherwise, &mut candidates, &mut fake_borrows, ); + if let Some(otherwise_block) = otherwise { + let source_info = self.source_info(scrutinee_span); + self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); + } + + let mut next_prebinding_block = None; + + for candidate in candidates.iter_mut().rev() { + candidate.next_candidate_pre_binding_block = next_prebinding_block; + next_prebinding_block = candidate.pre_binding_block; + } + if let Some(ref borrows) = fake_borrows { self.calculate_fake_borrows(borrows, scrutinee_span) } else { @@ -427,13 +426,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // create a dummy candidate let mut candidate = Candidate { span: irrefutable_pat.span, + has_guard: false, match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)], bindings: vec![], ascriptions: vec![], // since we don't call `match_candidates`, next fields are unused otherwise_block: None, - pre_binding_block: block, + pre_binding_block: None, next_candidate_pre_binding_block: None, }; @@ -645,6 +645,8 @@ crate struct Candidate<'pat, 'tcx> { // span of the original pattern that gave rise to this candidate span: Span, + has_guard: bool, + // all of these must be satisfied... match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, @@ -658,7 +660,7 @@ crate struct Candidate<'pat, 'tcx> { otherwise_block: Option, // ...and the blocks for add false edges between candidates - pre_binding_block: BasicBlock, + pre_binding_block: Option, next_candidate_pre_binding_block: Option, } @@ -758,13 +760,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// which of these candidates, if any, is the correct one. The /// candidates are sorted such that the first item in the list /// has the highest priority. When a candidate is found to match - /// the value, we will generate a branch to the appropriate + /// the value, we will set and generate a branch to the appropriate /// prebinding block. /// /// If we find that *NONE* of the candidates apply, we branch to the - /// `otherwise_block`. In principle, this means that the input list was not - /// exhaustive, though at present we sometimes are not smart enough to - /// recognize all exhaustive inputs. + /// `otherwise_block`, setting it to `Some` if required. In principle, this + /// means that the input list was not exhaustive, though at present we + /// sometimes are not smart enough to recognize all exhaustive inputs. /// /// It might be surprising that the input can be inexhaustive. /// Indeed, initially, it is not, because all matches are @@ -778,8 +780,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn match_candidates<'pat>( &mut self, span: Span, - start_block: &mut Option, - otherwise_block: Option, + start_block: BasicBlock, + otherwise_block: &mut Option, candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option>>, ) { @@ -802,7 +804,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("match_candidates: {:?} candidates fully matched", fully_matched); let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); - let block: BasicBlock = if !matched_candidates.is_empty() { + let block = if !matched_candidates.is_empty() { let otherwise_block = self.select_matched_candidates(matched_candidates, start_block, fake_borrows); @@ -816,7 +818,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.start_new_block() } } else { - *start_block.get_or_insert_with(|| self.cfg.start_new_block()) + start_block }; // If there are no candidates that still need testing, we're @@ -824,9 +826,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // never reach this point. if unmatched_candidates.is_empty() { let source_info = self.source_info(span); - match otherwise_block { - Some(otherwise) => self.cfg.goto(block, source_info, otherwise), - None => self.cfg.terminate(block, source_info, TerminatorKind::Unreachable), + if let Some(otherwise) = *otherwise_block { + self.cfg.goto(block, source_info, otherwise); + } else { + *otherwise_block = Some(block); } return; } @@ -856,7 +859,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn select_matched_candidates( &mut self, matched_candidates: &mut [&mut Candidate<'_, 'tcx>], - start_block: &mut Option, + start_block: BasicBlock, fake_borrows: &mut Option>>, ) -> Option { debug_assert!( @@ -899,66 +902,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fully_matched_with_guard = matched_candidates .iter() - .position(|c| c.otherwise_block.is_none()) + .position(|c| !c.has_guard) .unwrap_or(matched_candidates.len() - 1); let (reachable_candidates, unreachable_candidates) = matched_candidates.split_at_mut(fully_matched_with_guard + 1); - let first_candidate = &reachable_candidates[0]; - let first_prebinding_block = first_candidate.pre_binding_block; + let mut next_prebinding = start_block; - // `goto -> first_prebinding_block` from the `start_block` if there is one. - if let Some(start_block) = *start_block { - let source_info = self.source_info(first_candidate.span); - self.cfg.goto(start_block, source_info, first_prebinding_block); - } else { - *start_block = Some(first_prebinding_block); - } - - for (first_candidate, second_candidate) in reachable_candidates.iter().tuple_windows() { - let source_info = self.source_info(first_candidate.span); - if let Some(otherwise_block) = first_candidate.otherwise_block { - self.false_edges( - otherwise_block, - second_candidate.pre_binding_block, - first_candidate.next_candidate_pre_binding_block, - source_info, - ); - } else { - bug!("candidate other than the last has no guard"); + for candidate in reachable_candidates.iter_mut() { + assert!(candidate.otherwise_block.is_none()); + assert!(candidate.pre_binding_block.is_none()); + candidate.pre_binding_block = Some(next_prebinding); + if candidate.has_guard { + next_prebinding = self.cfg.start_new_block(); + candidate.otherwise_block = Some(next_prebinding); } } - debug!("match_candidates: add false edges for unreachable {:?}", unreachable_candidates); + debug!( + "match_candidates: add pre_binding_blocks for unreachable {:?}", + unreachable_candidates, + ); for candidate in unreachable_candidates { - if let Some(otherwise) = candidate.otherwise_block { - let source_info = self.source_info(candidate.span); - let unreachable = self.cfg.start_new_block(); - self.false_edges( - otherwise, - unreachable, - candidate.next_candidate_pre_binding_block, - source_info, - ); - self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); - } + assert!(candidate.pre_binding_block.is_none()); + candidate.pre_binding_block = Some(self.cfg.start_new_block()); } - let last_candidate = reachable_candidates.last().unwrap(); - if let Some(otherwise) = last_candidate.otherwise_block { - let source_info = self.source_info(last_candidate.span); - let block = self.cfg.start_new_block(); - self.false_edges( - otherwise, - block, - last_candidate.next_candidate_pre_binding_block, - source_info, - ); - Some(block) - } else { - None - } + reachable_candidates.last_mut().unwrap().otherwise_block } /// This is the most subtle part of the matching algorithm. At @@ -1078,7 +1049,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], block: BasicBlock, - mut otherwise_block: Option, + otherwise_block: &mut Option, fake_borrows: &mut Option>>, ) { // extract the match-pair from the highest priority candidate @@ -1150,12 +1121,39 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // improves the speed of llvm when optimizing long string literal // matches let make_target_blocks = move |this: &mut Self| -> Vec { + // The block that we should branch to if none of the + // `target_candidates` match. This is either the block where we + // start matching the untested candidates if there are any, + // otherwise it's the `otherwise_block`. + let remainder_start = &mut None; + let remainder_start = + if candidates.is_empty() { &mut *otherwise_block } else { remainder_start }; + // For each outcome of test, process the candidates that still // apply. Collect a list of blocks where control flow will // branch if one of the `target_candidate` sets is not // exhaustive. + let target_blocks: Vec<_> = target_candidates + .into_iter() + .map(|mut candidates| { + if candidates.len() != 0 { + let candidate_start = this.cfg.start_new_block(); + this.match_candidates( + span, + candidate_start, + remainder_start, + &mut *candidates, + fake_borrows, + ); + candidate_start + } else { + *remainder_start.get_or_insert_with(|| this.cfg.start_new_block()) + } + }) + .collect(); + if !candidates.is_empty() { - let remainder_start = &mut None; + let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block()); this.match_candidates( span, remainder_start, @@ -1163,36 +1161,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidates, fake_borrows, ); - otherwise_block = Some(remainder_start.unwrap()); }; - target_candidates - .into_iter() - .map(|mut candidates| { - if candidates.len() != 0 { - let candidate_start = &mut None; - this.match_candidates( - span, - candidate_start, - otherwise_block, - &mut *candidates, - fake_borrows, - ); - candidate_start.unwrap() - } else { - *otherwise_block.get_or_insert_with(|| { - let unreachable = this.cfg.start_new_block(); - let source_info = this.source_info(span); - this.cfg.terminate( - unreachable, - source_info, - TerminatorKind::Unreachable, - ); - unreachable - }) - } - }) - .collect() + target_blocks }; self.perform_test(block, &match_place, &test, make_target_blocks); @@ -1297,7 +1268,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let candidate_source_info = self.source_info(candidate.span); - let mut block = candidate.pre_binding_block; + let mut block = candidate.pre_binding_block.unwrap(); // If we are adding our own statements, then we need a fresh block. let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some() @@ -1437,11 +1408,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp)); } + let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| { + let unreachable = self.cfg.start_new_block(); + self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); + unreachable + }); + let outside_scope = self.cfg.start_new_block(); self.exit_scope( source_info.span, region_scope, otherwise_post_guard_block, - candidate.otherwise_block.unwrap(), + outside_scope, + ); + self.false_edges( + outside_scope, + otherwise_block, + candidate.next_candidate_pre_binding_block, + source_info, ); // We want to ensure that the matched candidates are bound