mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-03 13:37:37 +00:00
Rollup merge of #126553 - Nadrieril:expand-or-pat-into-above, r=matthewjasper
match lowering: expand or-candidates mixed with candidates above This PR tweaks match lowering of or-patterns. Consider this: ```rust match (x, y) { (1, true) => 1, (2, false) => 2, (1 | 2, true | false) => 3, (3 | 4, true | false) => 4, _ => 5, } ``` One might hope that this can be compiled to a single `SwitchInt` on `x` followed by some boolean checks. Before this PR, we compile this to 3 `SwitchInt`s on `x`, because an arm that contains more than one or-pattern was compiled on its own. This PR groups branch `3` with the two branches above, getting us down to 2 `SwitchInt`s on `x`. We can't in general expand or-patterns freely, because this interacts poorly with another optimization we do: or-pattern simplification. When an or-pattern doesn't involve bindings, we branch the success paths of all its alternatives to the same block. The drawback is that in a case like: ```rust match (1, true) { (1 | 2, false) => unreachable!(), (2, _) => unreachable!(), _ => {} } ``` if we used a single `SwitchInt`, by the time we test `false` we don't know whether we came from the `1` case or the `2` case, so we don't know where to go if `false` doesn't match. Hence the limitation: we can process or-pattern alternatives alongside candidates that precede it, but not candidates that follow it. (Unless the or-pattern is the only remaining match pair of its candidate, in which case we can process it alongside whatever). This PR allows the processing of or-pattern alternatives alongside candidates that precede it. One benefit is that we now process or-patterns in a single place in `mod.rs`. r? ``@matthewjasper``
This commit is contained in:
commit
e111e99253
@ -1074,12 +1074,9 @@ struct Candidate<'pat, 'tcx> {
|
|||||||
// because that would break binding consistency.
|
// because that would break binding consistency.
|
||||||
subcandidates: Vec<Candidate<'pat, 'tcx>>,
|
subcandidates: Vec<Candidate<'pat, 'tcx>>,
|
||||||
|
|
||||||
/// ...and the guard must be evaluated if there is one.
|
/// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`.
|
||||||
has_guard: bool,
|
has_guard: bool,
|
||||||
|
|
||||||
/// If the guard is `false` then branch to `otherwise_block`.
|
|
||||||
otherwise_block: Option<BasicBlock>,
|
|
||||||
|
|
||||||
/// If the candidate matches, bindings and ascriptions must be established.
|
/// If the candidate matches, bindings and ascriptions must be established.
|
||||||
extra_data: PatternExtraData<'tcx>,
|
extra_data: PatternExtraData<'tcx>,
|
||||||
|
|
||||||
@ -1090,6 +1087,9 @@ struct Candidate<'pat, 'tcx> {
|
|||||||
/// The block before the `bindings` have been established.
|
/// The block before the `bindings` have been established.
|
||||||
pre_binding_block: Option<BasicBlock>,
|
pre_binding_block: Option<BasicBlock>,
|
||||||
|
|
||||||
|
/// The block to branch to if the guard or a nested candidate fails to match.
|
||||||
|
otherwise_block: Option<BasicBlock>,
|
||||||
|
|
||||||
/// The earliest block that has only candidates >= this one as descendents. Used for false
|
/// The earliest block that has only candidates >= this one as descendents. Used for false
|
||||||
/// edges, see the doc for [`Builder::match_expr`].
|
/// edges, see the doc for [`Builder::match_expr`].
|
||||||
false_edge_start_block: Option<BasicBlock>,
|
false_edge_start_block: Option<BasicBlock>,
|
||||||
@ -1364,56 +1364,105 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
otherwise_block: BasicBlock,
|
otherwise_block: BasicBlock,
|
||||||
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
candidates: &mut [&mut Candidate<'pat, 'tcx>],
|
||||||
) {
|
) {
|
||||||
let mut split_or_candidate = false;
|
// We process or-patterns here. If any candidate starts with an or-pattern, we have to
|
||||||
for candidate in &mut *candidates {
|
// expand the or-pattern before we can proceed further.
|
||||||
if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs {
|
//
|
||||||
// Split a candidate in which the only match-pair is an or-pattern into multiple
|
// We can't expand them freely however. The rule is: if the candidate has an or-pattern as
|
||||||
// candidates. This is so that
|
// its only remaining match pair, we can expand it freely. If it has other match pairs, we
|
||||||
//
|
// can expand it but we can't process more candidates after it.
|
||||||
// match x {
|
//
|
||||||
// 0 | 1 => { ... },
|
// If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the following,
|
||||||
// 2 | 3 => { ... },
|
// or-pattern simplification (in `merge_trivial_subcandidates`) makes it so the `1` and `2`
|
||||||
// }
|
// cases branch to a same block (which then tests `false`). If we took `(2, _)` in the same
|
||||||
//
|
// set of candidates, when we reach the block that tests `false` we don't know whether we
|
||||||
// only generates a single switch.
|
// came from `1` or `2`, hence we can't know where to branch on failure.
|
||||||
let match_pair = candidate.match_pairs.pop().unwrap();
|
// ```ignore(illustrative)
|
||||||
self.create_or_subcandidates(candidate, match_pair);
|
// match (1, true) {
|
||||||
split_or_candidate = true;
|
// (1 | 2, false) => {},
|
||||||
|
// (2, _) => {},
|
||||||
|
// _ => {}
|
||||||
|
// }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// We therefore split the `candidates` slice in two, expand or-patterns in the first half,
|
||||||
|
// and process both halves separately.
|
||||||
|
let mut expand_until = 0;
|
||||||
|
for (i, candidate) in candidates.iter().enumerate() {
|
||||||
|
if matches!(
|
||||||
|
&*candidate.match_pairs,
|
||||||
|
[MatchPair { test_case: TestCase::Or { .. }, .. }, ..]
|
||||||
|
) {
|
||||||
|
expand_until = i + 1;
|
||||||
|
if candidate.match_pairs.len() > 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
|
||||||
|
|
||||||
ensure_sufficient_stack(|| {
|
ensure_sufficient_stack(|| {
|
||||||
if split_or_candidate {
|
if candidates_to_expand.is_empty() {
|
||||||
// At least one of the candidates has been split into subcandidates.
|
// No candidates start with an or-pattern, we can continue.
|
||||||
// We need to change the candidate list to include those.
|
self.match_expanded_candidates(
|
||||||
let mut new_candidates = Vec::new();
|
span,
|
||||||
for candidate in candidates.iter_mut() {
|
scrutinee_span,
|
||||||
candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
|
start_block,
|
||||||
|
otherwise_block,
|
||||||
|
remaining_candidates,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Expand one level of or-patterns for each candidate in `candidates_to_expand`.
|
||||||
|
let mut expanded_candidates = Vec::new();
|
||||||
|
for candidate in candidates_to_expand.iter_mut() {
|
||||||
|
if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] =
|
||||||
|
&*candidate.match_pairs
|
||||||
|
{
|
||||||
|
let or_match_pair = candidate.match_pairs.remove(0);
|
||||||
|
// Expand the or-pattern into subcandidates.
|
||||||
|
self.create_or_subcandidates(candidate, or_match_pair);
|
||||||
|
// Collect the newly created subcandidates.
|
||||||
|
for subcandidate in candidate.subcandidates.iter_mut() {
|
||||||
|
expanded_candidates.push(subcandidate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
expanded_candidates.push(candidate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process the expanded candidates.
|
||||||
|
let remainder_start = self.cfg.start_new_block();
|
||||||
|
// There might be new or-patterns obtained from expanding the old ones, so we call
|
||||||
|
// `match_candidates` again.
|
||||||
self.match_candidates(
|
self.match_candidates(
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
start_block,
|
start_block,
|
||||||
otherwise_block,
|
remainder_start,
|
||||||
&mut *new_candidates,
|
expanded_candidates.as_mut_slice(),
|
||||||
);
|
);
|
||||||
|
|
||||||
for candidate in candidates {
|
// Simplify subcandidates and process any leftover match pairs.
|
||||||
self.merge_trivial_subcandidates(candidate);
|
for candidate in candidates_to_expand {
|
||||||
|
if !candidate.subcandidates.is_empty() {
|
||||||
|
self.finalize_or_candidate(span, scrutinee_span, candidate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.match_simplified_candidates(
|
// Process the remaining candidates.
|
||||||
|
self.match_candidates(
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
start_block,
|
remainder_start,
|
||||||
otherwise_block,
|
otherwise_block,
|
||||||
candidates,
|
remaining_candidates,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_simplified_candidates(
|
/// Construct the decision tree for `candidates`. Caller must ensure that no candidate in
|
||||||
|
/// `candidates` starts with an or-pattern.
|
||||||
|
fn match_expanded_candidates(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
scrutinee_span: Span,
|
scrutinee_span: Span,
|
||||||
@ -1438,7 +1487,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
// The first candidate has satisfied all its match pairs; we link it up and continue
|
// The first candidate has satisfied all its match pairs; we link it up and continue
|
||||||
// with the remaining candidates.
|
// with the remaining candidates.
|
||||||
start_block = self.select_matched_candidate(first, start_block);
|
start_block = self.select_matched_candidate(first, start_block);
|
||||||
self.match_simplified_candidates(
|
self.match_expanded_candidates(
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
start_block,
|
start_block,
|
||||||
@ -1448,7 +1497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
candidates => {
|
candidates => {
|
||||||
// The first candidate has some unsatisfied match pairs; we proceed to do more tests.
|
// The first candidate has some unsatisfied match pairs; we proceed to do more tests.
|
||||||
self.test_candidates_with_or(
|
self.test_candidates(
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
candidates,
|
candidates,
|
||||||
@ -1495,16 +1544,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
|
|
||||||
candidate.pre_binding_block = Some(start_block);
|
candidate.pre_binding_block = Some(start_block);
|
||||||
let otherwise_block = self.cfg.start_new_block();
|
let otherwise_block = self.cfg.start_new_block();
|
||||||
if candidate.has_guard {
|
// Create the otherwise block for this candidate, which is the
|
||||||
// Create the otherwise block for this candidate, which is the
|
// pre-binding block for the next candidate.
|
||||||
// pre-binding block for the next candidate.
|
candidate.otherwise_block = Some(otherwise_block);
|
||||||
candidate.otherwise_block = Some(otherwise_block);
|
|
||||||
}
|
|
||||||
otherwise_block
|
otherwise_block
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tests a candidate where there are only or-patterns left to test, or
|
/// Simplify subcandidates and process any leftover match pairs. The candidate should have been
|
||||||
/// forwards to [Builder::test_candidates].
|
/// expanded with `create_or_subcandidates`.
|
||||||
///
|
///
|
||||||
/// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
|
/// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
|
||||||
/// so:
|
/// so:
|
||||||
@ -1556,84 +1603,56 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
/// |
|
/// |
|
||||||
/// ...
|
/// ...
|
||||||
/// ```
|
/// ```
|
||||||
fn test_candidates_with_or(
|
fn finalize_or_candidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
scrutinee_span: Span,
|
scrutinee_span: Span,
|
||||||
candidates: &mut [&mut Candidate<'_, 'tcx>],
|
candidate: &mut Candidate<'_, 'tcx>,
|
||||||
start_block: BasicBlock,
|
|
||||||
otherwise_block: BasicBlock,
|
|
||||||
) {
|
) {
|
||||||
let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
|
if candidate.subcandidates.is_empty() {
|
||||||
assert!(first_candidate.subcandidates.is_empty());
|
|
||||||
if !matches!(first_candidate.match_pairs[0].test_case, TestCase::Or { .. }) {
|
|
||||||
self.test_candidates(span, scrutinee_span, candidates, start_block, otherwise_block);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let first_match_pair = first_candidate.match_pairs.remove(0);
|
self.merge_trivial_subcandidates(candidate);
|
||||||
let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs);
|
|
||||||
let remainder_start = self.cfg.start_new_block();
|
|
||||||
// Test the alternatives of this or-pattern.
|
|
||||||
self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair);
|
|
||||||
|
|
||||||
if !remaining_match_pairs.is_empty() {
|
if !candidate.match_pairs.is_empty() {
|
||||||
// If more match pairs remain, test them after each subcandidate.
|
// If more match pairs remain, test them after each subcandidate.
|
||||||
// We could add them to the or-candidates before the call to `test_or_pattern` but this
|
// We could add them to the or-candidates before the call to `test_or_pattern` but this
|
||||||
// would make it impossible to detect simplifiable or-patterns. That would guarantee
|
// would make it impossible to detect simplifiable or-patterns. That would guarantee
|
||||||
// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
|
// exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
|
||||||
first_candidate.visit_leaves(|leaf_candidate| {
|
let mut last_otherwise = None;
|
||||||
|
candidate.visit_leaves(|leaf_candidate| {
|
||||||
|
last_otherwise = leaf_candidate.otherwise_block;
|
||||||
|
});
|
||||||
|
let remaining_match_pairs = mem::take(&mut candidate.match_pairs);
|
||||||
|
candidate.visit_leaves(|leaf_candidate| {
|
||||||
assert!(leaf_candidate.match_pairs.is_empty());
|
assert!(leaf_candidate.match_pairs.is_empty());
|
||||||
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
|
leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
|
||||||
let or_start = leaf_candidate.pre_binding_block.unwrap();
|
let or_start = leaf_candidate.pre_binding_block.unwrap();
|
||||||
// In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b,
|
// In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
|
||||||
// c | d)` will fail too. If there is no guard, we skip testing of `b` by branching
|
// R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
|
||||||
// directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`.
|
// directly to `last_otherwise`. If there is a guard,
|
||||||
let or_otherwise = leaf_candidate.otherwise_block.unwrap_or(remainder_start);
|
// `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
|
||||||
self.test_candidates_with_or(
|
// can't skip `Q`.
|
||||||
|
let or_otherwise = if leaf_candidate.has_guard {
|
||||||
|
leaf_candidate.otherwise_block.unwrap()
|
||||||
|
} else {
|
||||||
|
last_otherwise.unwrap()
|
||||||
|
};
|
||||||
|
self.match_candidates(
|
||||||
span,
|
span,
|
||||||
scrutinee_span,
|
scrutinee_span,
|
||||||
&mut [leaf_candidate],
|
|
||||||
or_start,
|
or_start,
|
||||||
or_otherwise,
|
or_otherwise,
|
||||||
|
&mut [leaf_candidate],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test the remaining candidates.
|
|
||||||
self.match_candidates(
|
|
||||||
span,
|
|
||||||
scrutinee_span,
|
|
||||||
remainder_start,
|
|
||||||
otherwise_block,
|
|
||||||
remaining_candidates,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")]
|
|
||||||
fn test_or_pattern<'pat>(
|
|
||||||
&mut self,
|
|
||||||
candidate: &mut Candidate<'pat, 'tcx>,
|
|
||||||
start_block: BasicBlock,
|
|
||||||
otherwise_block: BasicBlock,
|
|
||||||
match_pair: MatchPair<'pat, 'tcx>,
|
|
||||||
) {
|
|
||||||
let or_span = match_pair.pattern.span;
|
|
||||||
self.create_or_subcandidates(candidate, match_pair);
|
|
||||||
let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect();
|
|
||||||
self.match_candidates(
|
|
||||||
or_span,
|
|
||||||
or_span,
|
|
||||||
start_block,
|
|
||||||
otherwise_block,
|
|
||||||
&mut or_candidate_refs,
|
|
||||||
);
|
|
||||||
self.merge_trivial_subcandidates(candidate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
|
/// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
|
||||||
/// subcandidate. Any candidate that has been expanded that way should be passed to
|
/// subcandidate. Any candidate that has been expanded that way should be passed to
|
||||||
/// `merge_trivial_subcandidates` after its subcandidates have been processed.
|
/// `finalize_or_candidate` after its subcandidates have been processed.
|
||||||
fn create_or_subcandidates<'pat>(
|
fn create_or_subcandidates<'pat>(
|
||||||
&mut self,
|
&mut self,
|
||||||
candidate: &mut Candidate<'pat, 'tcx>,
|
candidate: &mut Candidate<'pat, 'tcx>,
|
||||||
@ -1651,8 +1670,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to merge all of the subcandidates of the given candidate into one. This avoids
|
/// 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, ...)`. The or-pattern should have
|
/// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been
|
||||||
/// been expanded with `create_or_subcandidates`.
|
/// expanded with `create_or_subcandidates`.
|
||||||
fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
|
fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
|
||||||
if candidate.subcandidates.is_empty() || candidate.has_guard {
|
if candidate.subcandidates.is_empty() || candidate.has_guard {
|
||||||
// FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
|
// FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
|
||||||
@ -1664,6 +1683,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
|
subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
|
||||||
});
|
});
|
||||||
if can_merge {
|
if can_merge {
|
||||||
|
let mut last_otherwise = None;
|
||||||
let any_matches = self.cfg.start_new_block();
|
let any_matches = self.cfg.start_new_block();
|
||||||
let or_span = candidate.or_span.take().unwrap();
|
let or_span = candidate.or_span.take().unwrap();
|
||||||
let source_info = self.source_info(or_span);
|
let source_info = self.source_info(or_span);
|
||||||
@ -1674,8 +1694,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
for subcandidate in mem::take(&mut candidate.subcandidates) {
|
for subcandidate in mem::take(&mut candidate.subcandidates) {
|
||||||
let or_block = subcandidate.pre_binding_block.unwrap();
|
let or_block = subcandidate.pre_binding_block.unwrap();
|
||||||
self.cfg.goto(or_block, source_info, any_matches);
|
self.cfg.goto(or_block, source_info, any_matches);
|
||||||
|
last_otherwise = subcandidate.otherwise_block;
|
||||||
}
|
}
|
||||||
candidate.pre_binding_block = Some(any_matches);
|
candidate.pre_binding_block = Some(any_matches);
|
||||||
|
assert!(last_otherwise.is_some());
|
||||||
|
candidate.otherwise_block = last_otherwise;
|
||||||
} else {
|
} else {
|
||||||
// Never subcandidates may have a set of bindings inconsistent with their siblings,
|
// Never subcandidates may have a set of bindings inconsistent with their siblings,
|
||||||
// which would break later code. So we filter them out. Note that we can't filter out
|
// which would break later code. So we filter them out. Note that we can't filter out
|
||||||
|
24
tests/mir-opt/or_pattern.rs
Normal file
24
tests/mir-opt/or_pattern.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// skip-filecheck
|
||||||
|
|
||||||
|
// EMIT_MIR or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir
|
||||||
|
fn shortcut_second_or() {
|
||||||
|
// Check that after matching `0`, failing to match `2 | 3` skips trying to match `(1, 2 | 3)`.
|
||||||
|
match ((0, 0), 0) {
|
||||||
|
(x @ (0, _) | x @ (_, 1), y @ 2 | y @ 3) => {}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EMIT_MIR or_pattern.single_switchint.SimplifyCfg-initial.after.mir
|
||||||
|
fn single_switchint() {
|
||||||
|
// Check how many `SwitchInt`s we do. In theory a single one is necessary.
|
||||||
|
match (1, true) {
|
||||||
|
(1, true) => 1,
|
||||||
|
(2, false) => 2,
|
||||||
|
(1 | 2, true | false) => 3,
|
||||||
|
(3 | 4, true | false) => 4,
|
||||||
|
_ => 5,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,100 @@
|
|||||||
|
// MIR for `shortcut_second_or` after SimplifyCfg-initial
|
||||||
|
|
||||||
|
fn shortcut_second_or() -> () {
|
||||||
|
let mut _0: ();
|
||||||
|
let mut _1: ((i32, i32), i32);
|
||||||
|
let mut _2: (i32, i32);
|
||||||
|
let _3: (i32, i32);
|
||||||
|
let _4: i32;
|
||||||
|
scope 1 {
|
||||||
|
debug x => _3;
|
||||||
|
debug y => _4;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
StorageLive(_2);
|
||||||
|
_2 = (const 0_i32, const 0_i32);
|
||||||
|
_1 = (move _2, const 0_i32);
|
||||||
|
StorageDead(_2);
|
||||||
|
PlaceMention(_1);
|
||||||
|
switchInt(((_1.0: (i32, i32)).0: i32)) -> [0: bb4, otherwise: bb2];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
_0 = const ();
|
||||||
|
goto -> bb14;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
switchInt(((_1.0: (i32, i32)).1: i32)) -> [1: bb3, otherwise: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
switchInt((_1.1: i32)) -> [2: bb7, 3: bb8, otherwise: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb4: {
|
||||||
|
switchInt((_1.1: i32)) -> [2: bb5, 3: bb6, otherwise: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb5: {
|
||||||
|
falseEdge -> [real: bb10, imaginary: bb6];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb6: {
|
||||||
|
falseEdge -> [real: bb11, imaginary: bb2];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb7: {
|
||||||
|
falseEdge -> [real: bb12, imaginary: bb8];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb8: {
|
||||||
|
falseEdge -> [real: bb13, imaginary: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb9: {
|
||||||
|
_0 = const ();
|
||||||
|
StorageDead(_4);
|
||||||
|
StorageDead(_3);
|
||||||
|
goto -> bb14;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb10: {
|
||||||
|
StorageLive(_3);
|
||||||
|
_3 = (_1.0: (i32, i32));
|
||||||
|
StorageLive(_4);
|
||||||
|
_4 = (_1.1: i32);
|
||||||
|
goto -> bb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb11: {
|
||||||
|
StorageLive(_3);
|
||||||
|
_3 = (_1.0: (i32, i32));
|
||||||
|
StorageLive(_4);
|
||||||
|
_4 = (_1.1: i32);
|
||||||
|
goto -> bb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb12: {
|
||||||
|
StorageLive(_3);
|
||||||
|
_3 = (_1.0: (i32, i32));
|
||||||
|
StorageLive(_4);
|
||||||
|
_4 = (_1.1: i32);
|
||||||
|
goto -> bb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb13: {
|
||||||
|
StorageLive(_3);
|
||||||
|
_3 = (_1.0: (i32, i32));
|
||||||
|
StorageLive(_4);
|
||||||
|
_4 = (_1.1: i32);
|
||||||
|
goto -> bb9;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb14: {
|
||||||
|
StorageDead(_1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
// MIR for `single_switchint` after SimplifyCfg-initial
|
||||||
|
|
||||||
|
fn single_switchint() -> () {
|
||||||
|
let mut _0: ();
|
||||||
|
let _1: i32;
|
||||||
|
let mut _2: (i32, bool);
|
||||||
|
|
||||||
|
bb0: {
|
||||||
|
StorageLive(_1);
|
||||||
|
StorageLive(_2);
|
||||||
|
_2 = (const 1_i32, const true);
|
||||||
|
PlaceMention(_2);
|
||||||
|
switchInt((_2.0: i32)) -> [1: bb2, 2: bb4, otherwise: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb1: {
|
||||||
|
switchInt((_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb2: {
|
||||||
|
switchInt((_2.1: bool)) -> [0: bb6, otherwise: bb3];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb3: {
|
||||||
|
falseEdge -> [real: bb9, imaginary: bb4];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb4: {
|
||||||
|
switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb6];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb5: {
|
||||||
|
falseEdge -> [real: bb10, imaginary: bb6];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb6: {
|
||||||
|
falseEdge -> [real: bb11, imaginary: bb1];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb7: {
|
||||||
|
_1 = const 5_i32;
|
||||||
|
goto -> bb13;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb8: {
|
||||||
|
falseEdge -> [real: bb12, imaginary: bb7];
|
||||||
|
}
|
||||||
|
|
||||||
|
bb9: {
|
||||||
|
_1 = const 1_i32;
|
||||||
|
goto -> bb13;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb10: {
|
||||||
|
_1 = const 2_i32;
|
||||||
|
goto -> bb13;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb11: {
|
||||||
|
_1 = const 3_i32;
|
||||||
|
goto -> bb13;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb12: {
|
||||||
|
_1 = const 4_i32;
|
||||||
|
goto -> bb13;
|
||||||
|
}
|
||||||
|
|
||||||
|
bb13: {
|
||||||
|
StorageDead(_2);
|
||||||
|
StorageDead(_1);
|
||||||
|
_0 = const ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
@ -26,5 +26,6 @@ fn main() {
|
|||||||
assert_eq!(or_at(Err(7)), 207);
|
assert_eq!(or_at(Err(7)), 207);
|
||||||
assert_eq!(or_at(Err(8)), 8);
|
assert_eq!(or_at(Err(8)), 8);
|
||||||
assert_eq!(or_at(Err(20)), 220);
|
assert_eq!(or_at(Err(20)), 220);
|
||||||
|
assert_eq!(or_at(Err(34)), 134);
|
||||||
assert_eq!(or_at(Err(50)), 500);
|
assert_eq!(or_at(Err(50)), 500);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/inner-or-pat.rs:38:54
|
--> $DIR/inner-or-pat.rs:36:54
|
||||||
|
|
|
|
||||||
LL | match x {
|
LL | match x {
|
||||||
| - this expression has type `&str`
|
| - this expression has type `&str`
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0408]: variable `x` is not bound in all patterns
|
error[E0408]: variable `x` is not bound in all patterns
|
||||||
--> $DIR/inner-or-pat.rs:53:37
|
--> $DIR/inner-or-pat.rs:51:37
|
||||||
|
|
|
|
||||||
LL | (x @ "red" | (x @ "blue" | "red")) => {
|
LL | (x @ "red" | (x @ "blue" | "red")) => {
|
||||||
| - ^^^^^ pattern doesn't bind `x`
|
| - ^^^^^ pattern doesn't bind `x`
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
//@ revisions: or1 or2 or3 or4 or5
|
//@ revisions: or1 or3 or4
|
||||||
//@ [or1] run-pass
|
//@ [or1] run-pass
|
||||||
//@ [or2] run-pass
|
|
||||||
//@ [or5] run-pass
|
|
||||||
|
|
||||||
#![allow(unreachable_patterns)]
|
#![allow(unreachable_patterns)]
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
//@ check-pass
|
//@ run-pass
|
||||||
|
|
||||||
#![deny(unreachable_patterns)]
|
#![deny(unreachable_patterns)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match (3,42) {
|
match (3, 42) {
|
||||||
(a,_) | (_,a) if a > 10 => {println!("{}", a)}
|
(a, _) | (_, a) if a > 10 => {}
|
||||||
_ => ()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
match Some((3,42)) {
|
match Some((3, 42)) {
|
||||||
Some((a, _)) | Some((_, a)) if a > 10 => {println!("{}", a)}
|
Some((a, _)) | Some((_, a)) if a > 10 => {}
|
||||||
_ => ()
|
_ => unreachable!(),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match Some((3,42)) {
|
match Some((3, 42)) {
|
||||||
Some((a, _) | (_, a)) if a > 10 => {println!("{}", a)}
|
Some((a, _) | (_, a)) if a > 10 => {}
|
||||||
_ => ()
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,23 @@ fn search_old_style(target: (bool, bool, bool)) -> u32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check that a dummy or-pattern also leads to running the guard multiple times.
|
||||||
|
fn search_with_dummy(target: (bool, bool)) -> u32 {
|
||||||
|
let x = ((false, true), (false, true), ());
|
||||||
|
let mut guard_count = 0;
|
||||||
|
match x {
|
||||||
|
((a, _) | (_, a), (b, _) | (_, b), _ | _)
|
||||||
|
if {
|
||||||
|
guard_count += 1;
|
||||||
|
(a, b) == target
|
||||||
|
} =>
|
||||||
|
{
|
||||||
|
guard_count
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
assert_eq!(search((false, false, false)), 1);
|
assert_eq!(search((false, false, false)), 1);
|
||||||
assert_eq!(search((false, false, true)), 2);
|
assert_eq!(search((false, false, true)), 2);
|
||||||
@ -60,4 +77,9 @@ fn main() {
|
|||||||
assert_eq!(search_old_style((true, false, true)), 6);
|
assert_eq!(search_old_style((true, false, true)), 6);
|
||||||
assert_eq!(search_old_style((true, true, false)), 7);
|
assert_eq!(search_old_style((true, true, false)), 7);
|
||||||
assert_eq!(search_old_style((true, true, true)), 8);
|
assert_eq!(search_old_style((true, true, true)), 8);
|
||||||
|
|
||||||
|
assert_eq!(search_with_dummy((false, false)), 1);
|
||||||
|
assert_eq!(search_with_dummy((false, true)), 3);
|
||||||
|
assert_eq!(search_with_dummy((true, false)), 5);
|
||||||
|
assert_eq!(search_with_dummy((true, true)), 7);
|
||||||
}
|
}
|
||||||
|
11
tests/ui/or-patterns/simplification_subtleties.rs
Normal file
11
tests/ui/or-patterns/simplification_subtleties.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
//@ run-pass
|
||||||
|
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
fn main() {
|
||||||
|
// Test that we don't naively sort the two `2`s together and confuse the failure paths.
|
||||||
|
match (1, true) {
|
||||||
|
(1 | 2, false | false) => unreachable!(),
|
||||||
|
(2, _) => unreachable!(),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user