Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions compiler/rustc_mir_build/src/build/matches/match_pair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,12 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {
let test_case = match pattern.kind {
PatKind::Wild | PatKind::Error(_) => default_irrefutable(),

PatKind::Or { ref pats } => TestCase::Or {
pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
},
PatKind::Or { ref pats } => {
let pats: Box<[_]> =
pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect();
let contains_bindings = pats.iter().any(|fpat| fpat.contains_bindings);
TestCase::Or { pats, contains_bindings }
}

PatKind::Range(ref range) => {
if range.is_full_range(cx.tcx) == Some(true) {
Expand Down Expand Up @@ -255,4 +258,10 @@ impl<'pat, 'tcx> MatchPairTree<'pat, 'tcx> {

MatchPairTree { place, test_case, subpairs, pattern }
}

/// Whether this recursively contains any bindings or ascriptions.
pub(super) fn contains_bindings(&self) -> bool {
matches!(self.test_case, TestCase::Or { contains_bindings: true, .. })
|| self.subpairs.iter().any(|p| p.contains_bindings())
}
}
690 changes: 432 additions & 258 deletions compiler/rustc_mir_build/src/build/matches/mod.rs

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions compiler/rustc_mir_build/src/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ use rustc_span::symbol::{sym, Symbol};
use rustc_span::{Span, DUMMY_SP};
use tracing::{debug, instrument};

use crate::build::matches::{Candidate, MatchPairTree, Test, TestBranch, TestCase, TestKind};
use crate::build::matches::{
Candidate, CandidateState, MatchPairTree, Test, TestBranch, TestCase, TestKind,
};
use crate::build::Builder;

impl<'a, 'tcx> Builder<'a, 'tcx> {
Expand Down Expand Up @@ -535,17 +537,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidate: &mut Candidate<'_, 'tcx>,
sorted_candidates: &FxIndexMap<TestBranch<'tcx>, Vec<&mut Candidate<'_, 'tcx>>>,
) -> Option<TestBranch<'tcx>> {
let CandidateState::Incomplete { match_pairs, .. } = &mut candidate.state else { bug!() };
// Find the match_pair for this place (if any). At present,
// afaik, there can be at most one. (In the future, if we
// adopted a more general `@` operator, there might be more
// than one, but it'd be very unusual to have two sides that
// both require tests; you'd expect one side to be simplified
// away.)
let (match_pair_index, match_pair) = candidate
.match_pairs
.iter()
.enumerate()
.find(|&(_, mp)| mp.place == Some(test_place))?;
let (match_pair_index, match_pair) =
match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == Some(test_place))?;

// If true, the match pair is completely entailed by its corresponding test
// branch, so it can be removed. If false, the match pair is _compatible_
Expand Down Expand Up @@ -581,12 +581,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
matches!(range.contains(value, self.tcx, self.param_env), None | Some(true))
})
};
let is_conflicting_candidate = |candidate: &&mut Candidate<'_, 'tcx>| {
candidate
.match_pairs
.iter()
.any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case))
};
let is_conflicting_candidate =
|candidate: &&mut Candidate<'_, 'tcx>| {
candidate.match_pairs().unwrap().iter().any(|mp| {
mp.place == Some(test_place) && is_covering_range(&mp.test_case)
})
};
if sorted_candidates
.get(&TestBranch::Failure)
.is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate))
Expand Down Expand Up @@ -756,10 +756,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {

if fully_matched {
// Replace the match pair by its sub-pairs.
let match_pair = candidate.match_pairs.remove(match_pair_index);
candidate.match_pairs.extend(match_pair.subpairs);
let match_pair = match_pairs.remove(match_pair_index);
match_pairs.extend(match_pair.subpairs);
// Move or-patterns to the end.
candidate.match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
match_pairs.sort_by_key(|pair| matches!(pair.test_case, TestCase::Or { .. }));
}

ret
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_mir_build/src/build/matches/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
for binding in &candidate.extra_data.bindings {
self.visit_binding(binding);
}
for match_pair in &candidate.match_pairs {
// This assumes the candidate state is `Incomplete`, which is the case since we collect fake
// borrows before starting the processing of candidates.
for match_pair in candidate.match_pairs().unwrap() {
self.visit_match_pair(match_pair);
}
}
Expand Down
12 changes: 0 additions & 12 deletions tests/mir-opt/building/match/exponential_or.rs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// MIR for `match_tuple` after SimplifyCfg-initial
// MIR for `exponential` after SimplifyCfg-initial

fn match_tuple(_1: (u32, bool, Option<i32>, u32)) -> u32 {
fn exponential(_1: (u32, bool, Option<i32>, u32)) -> u32 {
debug x => _1;
let mut _0: u32;
let mut _2: isize;
Expand Down
22 changes: 22 additions & 0 deletions tests/mir-opt/building/match/or_patterns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// skip-filecheck

// EMIT_MIR or_patterns.exponential.SimplifyCfg-initial.after.mir
fn exponential(x: (u32, bool, Option<i32>, u32)) -> u32 {
// Test that simple or-patterns don't get expanded to exponentially large CFGs
match x {
(y @ (1 | 4), true | false, Some(1 | 8) | None, z @ (6..=9 | 13..=16)) => y ^ z,
_ => 0,
}
}

// EMIT_MIR or_patterns.simplification_subtleties.built.after.mir
fn simplification_subtleties() {
// 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!(),
_ => {}
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// MIR for `simplification_subtleties` after built

fn simplification_subtleties() -> () {
let mut _0: ();
let mut _1: (i32, bool);
let mut _2: !;
let mut _3: !;

bb0: {
StorageLive(_1);
_1 = (const 1_i32, const true);
PlaceMention(_1);
switchInt((_1.0: i32)) -> [1: bb2, 2: bb4, otherwise: bb1];
}

bb1: {
switchInt((_1.0: i32)) -> [2: bb13, otherwise: bb12];
}

bb2: {
goto -> bb6;
}

bb3: {
goto -> bb1;
}

bb4: {
goto -> bb6;
}

bb5: {
goto -> bb1;
}

bb6: {
switchInt((_1.1: bool)) -> [0: bb8, otherwise: bb7];
}

bb7: {
goto -> bb5;
}

bb8: {
goto -> bb11;
}

bb9: {
goto -> bb11;
}

bb10: {
goto -> bb7;
}

bb11: {
falseEdge -> [real: bb17, imaginary: bb1];
}

bb12: {
_0 = const ();
goto -> bb20;
}

bb13: {
falseEdge -> [real: bb16, imaginary: bb12];
}

bb14: {
goto -> bb12;
}

bb15: {
FakeRead(ForMatchedPlace(None), _1);
unreachable;
}

bb16: {
StorageLive(_3);
_3 = core::panicking::panic(const "internal error: entered unreachable code") -> bb21;
}

bb17: {
StorageLive(_2);
_2 = core::panicking::panic(const "internal error: entered unreachable code") -> bb21;
}

bb18: {
StorageDead(_2);
goto -> bb20;
}

bb19: {
StorageDead(_3);
goto -> bb20;
}

bb20: {
StorageDead(_1);
return;
}

bb21 (cleanup): {
resume;
}
}
30 changes: 15 additions & 15 deletions tests/mir-opt/jump_threading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,29 @@ use std::ops::ControlFlow;
fn too_complex(x: Result<i32, usize>) -> Option<i32> {
// CHECK-LABEL: fn too_complex(
// CHECK: bb0: {
// CHECK: switchInt(move {{_.*}}) -> [0: bb3, 1: bb2, otherwise: bb1];
// CHECK: bb1: {
// CHECK: switchInt(move {{_.*}}) -> [0: [[continue1:bb.*]], 1: [[break1:bb.*]], otherwise: [[unreachable:bb.*]]];
// CHECK: [[unreachable]]: {
// CHECK: unreachable;
// CHECK: bb2: {
// CHECK: [[break1]]: {
// CHECK: [[controlflow:_.*]] = ControlFlow::<usize, i32>::Break(
// CHECK: goto -> bb8;
// CHECK: bb3: {
// CHECK: goto -> [[break2:bb.*]];
// CHECK: [[continue1]]: {
// CHECK: [[controlflow]] = ControlFlow::<usize, i32>::Continue(
// CHECK: goto -> bb4;
// CHECK: bb4: {
// CHECK: goto -> bb6;
// CHECK: bb5: {
// CHECK: goto -> [[continue2:bb.*]];
// CHECK: [[continue2]]: {
// CHECK: goto -> [[continue3:bb.*]];
// CHECK: [[break3:bb.*]]: {
// CHECK: {{_.*}} = (([[controlflow]] as Break).0: usize);
// CHECK: _0 = Option::<i32>::None;
// CHECK: goto -> bb7;
// CHECK: bb6: {
// CHECK: goto -> [[return:bb.*]];
// CHECK: [[continue3]]: {
// CHECK: {{_.*}} = (([[controlflow]] as Continue).0: i32);
// CHECK: _0 = Option::<i32>::Some(
// CHECK: goto -> bb7;
// CHECK: bb7: {
// CHECK: goto -> [[return]];
// CHECK: [[return]]: {
// CHECK: return;
// CHECK: bb8: {
// CHECK: goto -> bb5;
// CHECK: [[break2]]: {
// CHECK: goto -> [[break3]];
match {
match x {
Ok(v) => ControlFlow::Continue(v),
Expand Down
2 changes: 1 addition & 1 deletion tests/mir-opt/unreachable_enum_branching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ struct Plop {
fn simple() {
// CHECK-LABEL: fn simple(
// CHECK: [[discr:_.*]] = discriminant(
// CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: bb2, otherwise: [[unreachable]]];
// CHECK: switchInt(move [[discr]]) -> [0: [[unreachable:bb.*]], 1: [[unreachable]], 2: {{bb.*}}, otherwise: [[unreachable]]];
// CHECK: [[unreachable]]: {
// CHECK-NEXT: unreachable;
match Test1::C {
Expand Down