1
Fork 0

Rollup merge of #127159 - Nadrieril:hide-candidate, r=matthewjasper

match lowering: Hide `Candidate` from outside the lowering algorithm

The internals of `Candidate` are tricky and a source of confusion. This PR makes it so we don't expose `Candidate`s outside the lowering algorithm. Now:
- false edges are handled in `lower_match_tree`;
- `lower_match_tree` takes a list of patterns as input;
- `lower_match_tree` returns a flat datastructure that contains only the necessary information.

r? ```@matthewjasper```
This commit is contained in:
Matthias Krüger 2024-07-31 23:20:10 +02:00 committed by GitHub
commit 19f6ff0655
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
54 changed files with 1007 additions and 943 deletions

View file

@ -17,7 +17,6 @@ use rustc_span::symbol::Symbol;
use rustc_span::{BytePos, Pos, Span};
use rustc_target::abi::VariantIdx;
use tracing::{debug, instrument};
use util::visit_bindings;
use crate::build::expr::as_place::PlaceBuilder;
use crate::build::scope::DropKind;
@ -366,28 +365,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let scrutinee_place =
unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span));
let mut arm_candidates = self.create_match_candidates(&scrutinee_place, arms);
let match_has_guard = arm_candidates.iter().any(|(_, candidate)| candidate.has_guard);
let mut candidates =
arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
let arms = arms.iter().map(|arm| &self.thir[*arm]);
let match_start_span = span.shrink_to_lo().to(scrutinee_span);
// The set of places that we are creating fake borrows of. If there are no match guards then
// we don't need any fake borrows, so don't track them.
let fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
util::collect_fake_borrows(self, &candidates, scrutinee_span, scrutinee_place.base())
} else {
Vec::new()
};
self.lower_match_tree(
let patterns = arms
.clone()
.map(|arm| {
let has_match_guard =
if arm.guard.is_some() { HasMatchGuard::Yes } else { HasMatchGuard::No };
(&*arm.pattern, has_match_guard)
})
.collect();
let built_tree = self.lower_match_tree(
block,
scrutinee_span,
&scrutinee_place,
match_start_span,
&mut candidates,
patterns,
false,
);
@ -395,9 +388,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
destination,
scrutinee_place,
scrutinee_span,
arm_candidates,
arms,
built_tree,
self.source_info(span),
fake_borrow_temps,
)
}
@ -417,51 +410,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(scrutinee_place_builder)
}
/// Create the initial `Candidate`s for a `match` expression.
fn create_match_candidates<'pat>(
&mut self,
scrutinee: &PlaceBuilder<'tcx>,
arms: &'pat [ArmId],
) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)>
where
'a: 'pat,
{
// Assemble the initial list of candidates. These top-level candidates
// are 1:1 with the original match arms, but other parts of match
// lowering also introduce subcandidates (for subpatterns), and will
// also flatten candidates in some cases. So in general a list of
// candidates does _not_ necessarily correspond to a list of arms.
arms.iter()
.copied()
.map(|arm| {
let arm = &self.thir[arm];
let arm_has_guard = arm.guard.is_some();
let arm_candidate =
Candidate::new(scrutinee.clone(), &arm.pattern, arm_has_guard, self);
(arm, arm_candidate)
})
.collect()
}
/// Lower the bindings, guards and arm bodies of a `match` expression.
///
/// The decision tree should have already been created
/// (by [Builder::lower_match_tree]).
///
/// `outer_source_info` is the SourceInfo for the whole match.
fn lower_match_arms(
fn lower_match_arms<'pat>(
&mut self,
destination: Place<'tcx>,
scrutinee_place_builder: PlaceBuilder<'tcx>,
scrutinee_span: Span,
arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>,
arms: impl IntoIterator<Item = &'pat Arm<'tcx>>,
built_match_tree: BuiltMatchTree<'tcx>,
outer_source_info: SourceInfo,
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
) -> BlockAnd<()> {
let arm_end_blocks: Vec<BasicBlock> = arm_candidates
) -> BlockAnd<()>
where
'tcx: 'pat,
{
let arm_end_blocks: Vec<BasicBlock> = arms
.into_iter()
.map(|(arm, candidate)| {
debug!("lowering arm {:?}\ncandidate = {:?}", arm, candidate);
.zip(built_match_tree.branches)
.map(|(arm, branch)| {
debug!("lowering arm {:?}\ncorresponding branch = {:?}", arm, branch);
let arm_source_info = self.source_info(arm.span);
let arm_scope = (arm.scope, arm_source_info);
@ -494,8 +465,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let arm_block = this.bind_pattern(
outer_source_info,
candidate,
&fake_borrow_temps,
branch,
&built_match_tree.fake_borrow_temps,
scrutinee_span,
Some((arm, match_scope)),
EmitStorageLive::Yes,
@ -548,18 +519,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_pattern(
&mut self,
outer_source_info: SourceInfo,
candidate: Candidate<'_, 'tcx>,
branch: MatchTreeBranch<'tcx>,
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
scrutinee_span: Span,
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
emit_storage_live: EmitStorageLive,
) -> BasicBlock {
if candidate.subcandidates.is_empty() {
// Avoid generating another `BasicBlock` when we only have one
// candidate.
if branch.sub_branches.len() == 1 {
let [sub_branch] = branch.sub_branches.try_into().unwrap();
// Avoid generating another `BasicBlock` when we only have one sub branch.
self.bind_and_guard_matched_candidate(
candidate,
&[],
sub_branch,
fake_borrow_temps,
scrutinee_span,
arm_match_scope,
@ -587,35 +557,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// We keep a stack of all of the bindings and type ascriptions
// from 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_data| {
if let Some(arm) = arm {
self.clear_top_scope(arm.scope);
}
let binding_end = self.bind_and_guard_matched_candidate(
leaf_candidate,
parent_data,
fake_borrow_temps,
scrutinee_span,
arm_match_scope,
schedule_drops,
emit_storage_live,
);
if arm.is_none() {
schedule_drops = ScheduleDrops::No;
}
self.cfg.goto(binding_end, outer_source_info, target_block);
},
|inner_candidate, parent_data| {
parent_data.push(inner_candidate.extra_data);
inner_candidate.subcandidates.into_iter()
},
|parent_data| {
parent_data.pop();
},
);
for sub_branch in branch.sub_branches {
if let Some(arm) = arm {
self.clear_top_scope(arm.scope);
}
let binding_end = self.bind_and_guard_matched_candidate(
sub_branch,
fake_borrow_temps,
scrutinee_span,
arm_match_scope,
schedule_drops,
emit_storage_live,
);
if arm.is_none() {
schedule_drops = ScheduleDrops::No;
}
self.cfg.goto(binding_end, outer_source_info, target_block);
}
target_block
}
@ -725,7 +683,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
initializer: PlaceBuilder<'tcx>,
set_match_place: bool,
) -> BlockAnd<()> {
let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self);
let built_tree = self.lower_match_tree(
block,
irrefutable_pat.span,
&initializer,
irrefutable_pat.span,
vec![(irrefutable_pat, HasMatchGuard::No)],
false,
);
let [branch] = built_tree.branches.try_into().unwrap();
// For matches and function arguments, the place that is being matched
// can be set when creating the variables. But the place for
@ -746,7 +712,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// };
// ```
if let Some(place) = initializer.try_to_place(self) {
visit_bindings(&[&mut candidate], |binding: &Binding<'_>| {
// Because or-alternatives bind the same variables, we only explore the first one.
let first_sub_branch = branch.sub_branches.first().unwrap();
for binding in &first_sub_branch.bindings {
let local = self.var_local_id(binding.var_id, OutsideGuard);
if let LocalInfo::User(BindingForm::Var(VarBindingForm {
opt_match_place: Some((ref mut match_place, _)),
@ -757,21 +725,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} else {
bug!("Let binding to non-user variable.")
};
});
}
}
}
self.lower_match_tree(
block,
irrefutable_pat.span,
&initializer,
irrefutable_pat.span,
&mut [&mut candidate],
false,
);
self.bind_pattern(
self.source_info(irrefutable_pat.span),
candidate,
branch,
&[],
irrefutable_pat.span,
None,
@ -1152,20 +1112,21 @@ struct Candidate<'pat, 'tcx> {
/// The earliest block that has only candidates >= this one as descendents. Used for false
/// edges, see the doc for [`Builder::match_expr`].
false_edge_start_block: Option<BasicBlock>,
/// The `false_edge_start_block` of the next candidate.
next_candidate_start_block: Option<BasicBlock>,
}
impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
fn new(
place: PlaceBuilder<'tcx>,
pattern: &'pat Pat<'tcx>,
has_guard: bool,
has_guard: HasMatchGuard,
cx: &mut Builder<'_, 'tcx>,
) -> Self {
// Use `FlatPat` to build simplified match pairs, then immediately
// incorporate them into a new candidate.
Self::from_flat_pat(FlatPat::new(place, pattern, cx), has_guard)
Self::from_flat_pat(
FlatPat::new(place, pattern, cx),
matches!(has_guard, HasMatchGuard::Yes),
)
}
/// Incorporates an already-simplified [`FlatPat`] into a new candidate.
@ -1179,7 +1140,6 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
otherwise_block: None,
pre_binding_block: None,
false_edge_start_block: None,
next_candidate_start_block: None,
}
}
@ -1199,6 +1159,17 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
|_| {},
);
}
/// Visit the leaf candidates in reverse order.
fn visit_leaves_rev<'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().rev(),
|_| {},
);
}
}
/// A depth-first traversal of the `Candidate` and all of its recursive
@ -1409,12 +1380,114 @@ pub(crate) struct ArmHasGuard(pub(crate) bool);
///////////////////////////////////////////////////////////////////////////
// Main matching algorithm
/// A sub-branch in the output of match lowering. Match lowering has generated MIR code that will
/// branch to `success_block` when the matched value matches the corresponding pattern. If there is
/// a guard, its failure must continue to `otherwise_block`, which will resume testing patterns.
#[derive(Debug)]
struct MatchTreeSubBranch<'tcx> {
span: Span,
/// The block that is branched to if the corresponding subpattern matches.
success_block: BasicBlock,
/// The block to branch to if this arm had a guard and the guard fails.
otherwise_block: BasicBlock,
/// The bindings to set up in this sub-branch.
bindings: Vec<Binding<'tcx>>,
/// The ascriptions to set up in this sub-branch.
ascriptions: Vec<Ascription<'tcx>>,
/// Whether the sub-branch corresponds to a never pattern.
is_never: bool,
}
/// A branch in the output of match lowering.
#[derive(Debug)]
struct MatchTreeBranch<'tcx> {
sub_branches: Vec<MatchTreeSubBranch<'tcx>>,
}
/// The result of generating MIR for a pattern-matching expression. Each input branch/arm/pattern
/// gives rise to an output `MatchTreeBranch`. If one of the patterns matches, we branch to the
/// corresponding `success_block`. If none of the patterns matches, we branch to `otherwise_block`.
///
/// Each branch is made of one of more sub-branches, corresponding to or-patterns. E.g.
/// ```ignore(illustrative)
/// match foo {
/// (x, false) | (false, x) => {}
/// (true, true) => {}
/// }
/// ```
/// Here the first arm gives the first `MatchTreeBranch`, which has two sub-branches, one for each
/// alternative of the or-pattern. They are kept separate because each needs to bind `x` to a
/// different place.
#[derive(Debug)]
struct BuiltMatchTree<'tcx> {
branches: Vec<MatchTreeBranch<'tcx>>,
otherwise_block: BasicBlock,
/// If any of the branches had a guard, we collect here the places and locals to fakely borrow
/// to ensure match guards can't modify the values as we match them. For more details, see
/// [`util::collect_fake_borrows`].
fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)>,
}
impl<'tcx> MatchTreeSubBranch<'tcx> {
fn from_sub_candidate(
candidate: Candidate<'_, 'tcx>,
parent_data: &Vec<PatternExtraData<'tcx>>,
) -> Self {
debug_assert!(candidate.match_pairs.is_empty());
MatchTreeSubBranch {
span: candidate.extra_data.span,
success_block: candidate.pre_binding_block.unwrap(),
otherwise_block: candidate.otherwise_block.unwrap(),
bindings: parent_data
.iter()
.flat_map(|d| &d.bindings)
.chain(&candidate.extra_data.bindings)
.cloned()
.collect(),
ascriptions: parent_data
.iter()
.flat_map(|d| &d.ascriptions)
.cloned()
.chain(candidate.extra_data.ascriptions)
.collect(),
is_never: candidate.extra_data.is_never,
}
}
}
impl<'tcx> MatchTreeBranch<'tcx> {
fn from_candidate(candidate: Candidate<'_, 'tcx>) -> Self {
let mut sub_branches = Vec::new();
traverse_candidate(
candidate,
&mut Vec::new(),
&mut |candidate: Candidate<'_, '_>, parent_data: &mut Vec<PatternExtraData<'_>>| {
sub_branches.push(MatchTreeSubBranch::from_sub_candidate(candidate, parent_data));
},
|inner_candidate, parent_data| {
parent_data.push(inner_candidate.extra_data);
inner_candidate.subcandidates.into_iter()
},
|parent_data| {
parent_data.pop();
},
);
MatchTreeBranch { sub_branches }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum HasMatchGuard {
Yes,
No,
}
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// The entrypoint of the matching algorithm. Create the decision tree for the match expression,
/// starting from `block`.
///
/// Modifies `candidates` to store the bindings and type ascriptions for
/// that candidate.
/// `patterns` is a list of patterns, one for each arm. The associated boolean indicates whether
/// the arm has a guard.
///
/// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
/// or not (for `let` and `match`). In the refutable case we return the block to which we branch
@ -1425,31 +1498,76 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee_span: Span,
scrutinee_place_builder: &PlaceBuilder<'tcx>,
match_start_span: Span,
candidates: &mut [&mut Candidate<'pat, 'tcx>],
patterns: Vec<(&'pat Pat<'tcx>, HasMatchGuard)>,
refutable: bool,
) -> BasicBlock {
// This will generate code to test scrutinee_place and branch to the appropriate arm block.
// See the doc comment on `match_candidates` for why we have an otherwise block.
let otherwise_block =
self.match_candidates(match_start_span, scrutinee_span, block, candidates);
) -> BuiltMatchTree<'tcx>
where
'tcx: 'pat,
{
// Assemble the initial list of candidates. These top-level candidates are 1:1 with the
// input patterns, but other parts of match lowering also introduce subcandidates (for
// sub-or-patterns). So inside the algorithm, the candidates list may not correspond to
// match arms directly.
let mut candidates: Vec<Candidate<'_, '_>> = patterns
.into_iter()
.map(|(pat, has_guard)| {
Candidate::new(scrutinee_place_builder.clone(), pat, has_guard, self)
})
.collect();
// Link each leaf candidate to the `false_edge_start_block` of the next one.
let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
for candidate in candidates {
candidate.visit_leaves(|leaf_candidate| {
if let Some(ref mut prev) = previous_candidate {
assert!(leaf_candidate.false_edge_start_block.is_some());
prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
let fake_borrow_temps = util::collect_fake_borrows(
self,
&candidates,
scrutinee_span,
scrutinee_place_builder.base(),
);
// This will generate code to test scrutinee_place and branch to the appropriate arm block.
// If none of the arms match, we branch to `otherwise_block`. When lowering a `match`
// expression, exhaustiveness checking ensures that this block is unreachable.
let mut candidate_refs = candidates.iter_mut().collect::<Vec<_>>();
let otherwise_block =
self.match_candidates(match_start_span, scrutinee_span, block, &mut candidate_refs);
// Set up false edges so that the borrow-checker cannot make use of the specific CFG we
// generated. We falsely branch from each candidate to the one below it to make it as if we
// were testing match branches one by one in order. In the refutable case we also want a
// false edge to the final failure block.
let mut next_candidate_start_block = if refutable { Some(otherwise_block) } else { None };
for candidate in candidates.iter_mut().rev() {
let has_guard = candidate.has_guard;
candidate.visit_leaves_rev(|leaf_candidate| {
if let Some(next_candidate_start_block) = next_candidate_start_block {
let source_info = self.source_info(leaf_candidate.extra_data.span);
// Falsely branch to `next_candidate_start_block` before reaching pre_binding.
let old_pre_binding = leaf_candidate.pre_binding_block.unwrap();
let new_pre_binding = self.cfg.start_new_block();
self.false_edges(
old_pre_binding,
new_pre_binding,
next_candidate_start_block,
source_info,
);
leaf_candidate.pre_binding_block = Some(new_pre_binding);
if has_guard {
// Falsely branch to `next_candidate_start_block` also if the guard fails.
let new_otherwise = self.cfg.start_new_block();
let old_otherwise = leaf_candidate.otherwise_block.unwrap();
self.false_edges(
new_otherwise,
old_otherwise,
next_candidate_start_block,
source_info,
);
leaf_candidate.otherwise_block = Some(new_otherwise);
}
}
previous_candidate = Some(leaf_candidate);
assert!(leaf_candidate.false_edge_start_block.is_some());
next_candidate_start_block = leaf_candidate.false_edge_start_block;
});
}
if refutable {
// In refutable cases there's always at least one candidate, and we want a false edge to
// the failure block.
previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block)
} else {
if !refutable {
// Match checking ensures `otherwise_block` is actually unreachable in irrefutable
// cases.
let source_info = self.source_info(scrutinee_span);
@ -1479,7 +1597,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
}
otherwise_block
BuiltMatchTree {
branches: candidates.into_iter().map(MatchTreeBranch::from_candidate).collect(),
otherwise_block,
fake_borrow_temps,
}
}
/// The main match algorithm. It begins with a set of candidates `candidates` and has the job of
@ -2229,17 +2351,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) -> BlockAnd<()> {
let expr_span = self.thir[expr_id].span;
let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
let mut candidate = Candidate::new(scrutinee.clone(), pat, false, self);
let otherwise_block = self.lower_match_tree(
let built_tree = self.lower_match_tree(
block,
expr_span,
&scrutinee,
pat.span,
&mut [&mut candidate],
vec![(pat, HasMatchGuard::No)],
true,
);
let [branch] = built_tree.branches.try_into().unwrap();
self.break_for_else(otherwise_block, self.source_info(expr_span));
self.break_for_else(built_tree.otherwise_block, self.source_info(expr_span));
match declare_let_bindings {
DeclareLetBindings::Yes => {
@ -2261,7 +2383,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let success = self.bind_pattern(
self.source_info(pat.span),
candidate,
branch,
&[],
expr_span,
None,
@ -2269,7 +2391,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
// If branch coverage is enabled, record this branch.
self.visit_coverage_conditional_let(pat, success, otherwise_block);
self.visit_coverage_conditional_let(pat, success, built_tree.otherwise_block);
success.unit()
}
@ -2282,52 +2404,28 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// 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
/// moving the binding once the guard has evaluated to true (see below).
fn bind_and_guard_matched_candidate<'pat>(
fn bind_and_guard_matched_candidate(
&mut self,
candidate: Candidate<'pat, 'tcx>,
parent_data: &[PatternExtraData<'tcx>],
sub_branch: MatchTreeSubBranch<'tcx>,
fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
scrutinee_span: Span,
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
schedule_drops: ScheduleDrops,
emit_storage_live: EmitStorageLive,
) -> BasicBlock {
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
debug!("bind_and_guard_matched_candidate(subbranch={:?})", sub_branch);
debug_assert!(candidate.match_pairs.is_empty());
let block = sub_branch.success_block;
let candidate_source_info = self.source_info(candidate.extra_data.span);
let mut block = candidate.pre_binding_block.unwrap();
if candidate.next_candidate_start_block.is_some() {
let fresh_block = self.cfg.start_new_block();
self.false_edges(
block,
fresh_block,
candidate.next_candidate_start_block,
candidate_source_info,
);
block = fresh_block;
}
if candidate.extra_data.is_never {
if sub_branch.is_never {
// This arm has a dummy body, we don't need to generate code for it. `block` is already
// unreachable (except via false edge).
let source_info = self.source_info(candidate.extra_data.span);
let source_info = self.source_info(sub_branch.span);
self.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
return self.cfg.start_new_block();
}
let ascriptions = parent_data
.iter()
.flat_map(|d| &d.ascriptions)
.cloned()
.chain(candidate.extra_data.ascriptions);
let bindings =
parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
self.ascribe_types(block, ascriptions);
self.ascribe_types(block, sub_branch.ascriptions);
// Lower an instance of the arm guard (if present) for this candidate,
// and then perform bindings for the arm body.
@ -2338,9 +2436,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Bindings for guards require some extra handling to automatically
// insert implicit references/dereferences.
self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
self.bind_matched_candidate_for_guard(
block,
schedule_drops,
sub_branch.bindings.iter(),
);
let guard_frame = GuardFrame {
locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(),
locals: sub_branch
.bindings
.iter()
.map(|b| GuardFrameLocal::new(b.var_id))
.collect(),
};
debug!("entering guard building context: {:?}", guard_frame);
self.guard_context.push(guard_frame);
@ -2376,17 +2482,7 @@ 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
});
self.false_edges(
otherwise_post_guard_block,
otherwise_block,
candidate.next_candidate_start_block,
source_info,
);
self.cfg.goto(otherwise_post_guard_block, source_info, sub_branch.otherwise_block);
// We want to ensure that the matched candidates are bound
// after we have confirmed this candidate *and* any
@ -2414,8 +2510,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// ```
//
// and that is clearly not correct.
let by_value_bindings =
bindings.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
let by_value_bindings = sub_branch
.bindings
.iter()
.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
// Read all of the by reference bindings to ensure that the
// place they refer to can't be modified by the guard.
for binding in by_value_bindings.clone() {
@ -2443,7 +2541,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.bind_matched_candidate_for_arm_body(
block,
schedule_drops,
bindings,
sub_branch.bindings.iter(),
emit_storage_live,
);
block

View file

@ -1,5 +1,3 @@
use std::marker::PhantomData;
use rustc_data_structures::fx::FxIndexMap;
use rustc_middle::mir::*;
use rustc_middle::ty::Ty;
@ -18,18 +16,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
from_block: BasicBlock,
real_target: BasicBlock,
imaginary_target: Option<BasicBlock>,
imaginary_target: BasicBlock,
source_info: SourceInfo,
) {
match imaginary_target {
Some(target) if target != real_target => {
self.cfg.terminate(
from_block,
source_info,
TerminatorKind::FalseEdge { real_target, imaginary_target: target },
);
}
_ => self.cfg.goto(from_block, source_info, real_target),
if imaginary_target != real_target {
self.cfg.terminate(
from_block,
source_info,
TerminatorKind::FalseEdge { real_target, imaginary_target },
);
} else {
self.cfg.goto(from_block, source_info, real_target)
}
}
}
@ -71,10 +68,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
/// a MIR pass run after borrow checking.
pub(super) fn collect_fake_borrows<'tcx>(
cx: &mut Builder<'_, 'tcx>,
candidates: &[&mut Candidate<'_, 'tcx>],
candidates: &[Candidate<'_, 'tcx>],
temp_span: Span,
scrutinee_base: PlaceBase,
) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
if candidates.iter().all(|candidate| !candidate.has_guard) {
// Fake borrows are only used when there is a guard.
return Vec::new();
}
let mut collector =
FakeBorrowCollector { cx, scrutinee_base, fake_borrows: FxIndexMap::default() };
for candidate in candidates.iter() {
@ -222,57 +223,6 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
}
}
/// Visit all the bindings of these candidates. Because or-alternatives bind the same variables, we
/// only explore the first one of each or-pattern.
pub(super) fn visit_bindings<'tcx>(
candidates: &[&mut Candidate<'_, 'tcx>],
f: impl FnMut(&Binding<'tcx>),
) {
let mut visitor = BindingsVisitor { f, phantom: PhantomData };
for candidate in candidates.iter() {
visitor.visit_candidate(candidate);
}
}
pub(super) struct BindingsVisitor<'tcx, F> {
f: F,
phantom: PhantomData<&'tcx ()>,
}
impl<'tcx, F> BindingsVisitor<'tcx, F>
where
F: FnMut(&Binding<'tcx>),
{
fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
for binding in &candidate.extra_data.bindings {
(self.f)(binding)
}
for match_pair in &candidate.match_pairs {
self.visit_match_pair(match_pair);
}
}
fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) {
for binding in &flat_pat.extra_data.bindings {
(self.f)(binding)
}
for match_pair in &flat_pat.match_pairs {
self.visit_match_pair(match_pair);
}
}
fn visit_match_pair(&mut self, match_pair: &MatchPairTree<'_, 'tcx>) {
if let TestCase::Or { pats, .. } = &match_pair.test_case {
// All the or-alternatives should bind the same locals, so we only visit the first one.
self.visit_flat_pat(&pats[0])
} else {
for subpair in &match_pair.subpairs {
self.visit_match_pair(subpair);
}
}
}
}
#[must_use]
pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
match ref_mutability {