Skip to content

Commit 844409e

Browse files
committed
Auto merge of #67668 - matthewjasper:or-patterns, r=<try>
Implement MIR lowering for or-patterns This is the last thing needed to get meaningful run-pass tests for or-patterns. There probably need to be more tests before stabilizing this, but the most important cases should have been covered. Note: we can generate exponentially large MIR CFGs when using or-patterns containing bindings, type ascriptions, or that are for a match arm with a guard. `src/test/mir-opt/exponential-or.rs` shows the best case for what we currently do. cc #54883 closes #60350 closes #67514 cc @Centril r? @pnkfelix
2 parents 2ee25da + 232d661 commit 844409e

File tree

61 files changed

+1535
-836
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1535
-836
lines changed

src/librustc_feature/active.rs

-1
Original file line numberDiff line numberDiff line change
@@ -556,7 +556,6 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
556556
sym::impl_trait_in_bindings,
557557
sym::generic_associated_types,
558558
sym::const_generics,
559-
sym::or_patterns,
560559
sym::let_chains,
561560
sym::raw_dylib,
562561
];

src/librustc_mir/build/matches/mod.rs

+463-230
Large diffs are not rendered by default.

src/librustc_mir/build/matches/simplify.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,39 @@ use crate::build::Builder;
1717
use crate::hair::{self, *};
1818
use rustc::hir::RangeEnd;
1919
use rustc::mir::interpret::truncate;
20+
use rustc::mir::Place;
2021
use rustc::ty;
2122
use rustc::ty::layout::{Integer, IntegerExt, Size};
2223
use syntax::attr::{SignedInt, UnsignedInt};
2324

25+
use smallvec::smallvec;
2426
use std::mem;
2527

2628
impl<'a, 'tcx> Builder<'a, 'tcx> {
27-
pub fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
29+
/// Simplify a candidate so that all match pairs require a test.
30+
///
31+
/// This method will also split a candidate where the only match-pair is an
32+
/// or-pattern into multiple candidates. This is so that
33+
///
34+
/// match x {
35+
/// 0 | 1 => { ... },
36+
/// 2 | 3 => { ... },
37+
/// }
38+
///
39+
/// only generates a single switch. If this happens this method returns
40+
/// `true`.
41+
pub fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) -> bool {
2842
// repeatedly simplify match pairs until fixed point is reached
2943
loop {
3044
let match_pairs = mem::take(&mut candidate.match_pairs);
45+
46+
if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, ref place }] =
47+
*match_pairs
48+
{
49+
candidate.subcandidates = self.create_or_subcanidates(candidate, place, pats);
50+
return true;
51+
}
52+
3153
let mut changed = false;
3254
for match_pair in match_pairs {
3355
match self.simplify_match_pair(match_pair, candidate) {
@@ -40,11 +62,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4062
}
4163
}
4264
if !changed {
43-
return; // if we were not able to simplify any, done.
65+
// Move or-patterns to the end, because they can result in us
66+
// creating additional candidates, so we want to test them as
67+
// late as possible.
68+
candidate
69+
.match_pairs
70+
.sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
71+
return false; // if we were not able to simplify any, done.
4472
}
4573
}
4674
}
4775

76+
fn create_or_subcanidates<'pat>(
77+
&mut self,
78+
candidate: &Candidate<'pat, 'tcx>,
79+
place: &Place<'tcx>,
80+
pats: &'pat [Pat<'tcx>],
81+
) -> Vec<Candidate<'pat, 'tcx>> {
82+
pats.iter()
83+
.map(|pat| {
84+
let mut candidate = Candidate {
85+
span: pat.span,
86+
has_guard: candidate.has_guard,
87+
needs_otherwise_block: candidate.needs_otherwise_block,
88+
match_pairs: smallvec![MatchPair { place: place.clone(), pattern: pat }],
89+
bindings: vec![],
90+
ascriptions: vec![],
91+
subcandidates: vec![],
92+
otherwise_block: None,
93+
pre_binding_block: None,
94+
next_candidate_pre_binding_block: None,
95+
};
96+
self.simplify_candidate(&mut candidate);
97+
candidate
98+
})
99+
.collect()
100+
}
101+
48102
/// Tries to simplify `match_pair`, returning `Ok(())` if
49103
/// successful. If successful, new match pairs and bindings will
50104
/// have been pushed into the candidate. If no simplification is

src/librustc_mir/build/matches/test.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
7070
}
7171
}
7272

73-
PatKind::Or { .. } => self
74-
.hir
75-
.tcx()
76-
.sess
77-
.span_fatal(match_pair.pattern.span, "or-patterns are not fully implemented yet"),
73+
PatKind::Or { .. } => bug!("or-patterns should have already been handled"),
7874

7975
PatKind::AscribeUserType { .. }
8076
| PatKind::Array { .. }

src/librustc_mir/hair/mod.rs

-11
Original file line numberDiff line numberDiff line change
@@ -315,17 +315,6 @@ pub struct Arm<'tcx> {
315315
pub span: Span,
316316
}
317317

318-
impl Arm<'tcx> {
319-
// HACK(or_patterns; Centril | dlrobertson): Remove this and
320-
// correctly handle each case in which this method is used.
321-
pub fn top_pats_hack(&self) -> &[Pat<'tcx>] {
322-
match &*self.pattern.kind {
323-
PatKind::Or { pats } => pats,
324-
_ => std::slice::from_ref(&self.pattern),
325-
}
326-
}
327-
}
328-
329318
#[derive(Clone, Debug)]
330319
pub enum Guard<'tcx> {
331320
If(ExprRef<'tcx>),

src/test/mir-opt/const_prop/discriminant.rs

+10-10
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,18 @@ fn main() {
1010
// ...
1111
// _3 = std::option::Option::<bool>::Some(const true,);
1212
// _4 = discriminant(_3);
13-
// switchInt(move _4) -> [1isize: bb3, otherwise: bb2];
13+
// switchInt(move _4) -> [1isize: bb2, otherwise: bb1];
1414
// }
1515
// bb1: {
16-
// _2 = const 42i32;
16+
// _2 = const 10i32;
1717
// goto -> bb4;
1818
// }
1919
// bb2: {
20-
// _2 = const 10i32;
21-
// goto -> bb4;
20+
// switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3];
2221
// }
2322
// bb3: {
24-
// switchInt(((_3 as Some).0: bool)) -> [false: bb2, otherwise: bb1];
23+
// _2 = const 42i32;
24+
// goto -> bb4;
2525
// }
2626
// bb4: {
2727
// _1 = Add(move _2, const 0i32);
@@ -33,18 +33,18 @@ fn main() {
3333
// ...
3434
// _3 = const Scalar(0x01) : std::option::Option<bool>;
3535
// _4 = const 1isize;
36-
// switchInt(const 1isize) -> [1isize: bb3, otherwise: bb2];
36+
// switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1];
3737
// }
3838
// bb1: {
39-
// _2 = const 42i32;
39+
// _2 = const 10i32;
4040
// goto -> bb4;
4141
// }
4242
// bb2: {
43-
// _2 = const 10i32;
44-
// goto -> bb4;
43+
// switchInt(const true) -> [false: bb1, otherwise: bb3];
4544
// }
4645
// bb3: {
47-
// switchInt(const true) -> [false: bb2, otherwise: bb1];
46+
// _2 = const 42i32;
47+
// goto -> bb4;
4848
// }
4949
// bb4: {
5050
// _1 = Add(move _2, const 0i32);

src/test/mir-opt/exponential-or.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Test that simple or-patterns don't get expanded to exponentially large CFGs
2+
3+
// ignore-tidy-linelength
4+
5+
#![feature(or_patterns)]
6+
7+
fn match_tuple(x: (u32, bool, Option<i32>, u32)) -> u32 {
8+
match x {
9+
(y @ (1 | 4), true | false, Some(1 | 8) | None, z @ (6..=9 | 13..=16)) => y + 2 * z,
10+
_ => 0,
11+
}
12+
}
13+
14+
fn main() {}
15+
16+
// END RUST SOURCE
17+
18+
// START rustc.match_tuple.SimplifyCfg-initial.after.mir
19+
// scope 1 {
20+
// debug y => _7;
21+
// debug z => _8;
22+
// }
23+
// bb0: {
24+
// FakeRead(ForMatchedPlace, _1);
25+
// switchInt((_1.0: u32)) -> [1u32: bb2, 4u32: bb2, otherwise: bb1];
26+
// }
27+
// bb1: {
28+
// _0 = const 0u32;
29+
// goto -> bb10;
30+
// }
31+
// bb2: {
32+
// _2 = discriminant((_1.2: std::option::Option<i32>));
33+
// switchInt(move _2) -> [0isize: bb4, 1isize: bb3, otherwise: bb1];
34+
// }
35+
// bb3: {
36+
// switchInt((((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1i32: bb4, 8i32: bb4, otherwise: bb1];
37+
// }
38+
// bb4: {
39+
// _5 = Le(const 6u32, (_1.3: u32));
40+
// switchInt(move _5) -> [false: bb6, otherwise: bb5];
41+
// }
42+
// bb5: {
43+
// _6 = Le((_1.3: u32), const 9u32);
44+
// switchInt(move _6) -> [false: bb6, otherwise: bb8];
45+
// }
46+
// bb6: {
47+
// _3 = Le(const 13u32, (_1.3: u32));
48+
// switchInt(move _3) -> [false: bb1, otherwise: bb7];
49+
// }
50+
// bb7: {
51+
// _4 = Le((_1.3: u32), const 16u32);
52+
// switchInt(move _4) -> [false: bb1, otherwise: bb8];
53+
// }
54+
// bb8: {
55+
// falseEdges -> [real: bb9, imaginary: bb1];
56+
// }
57+
// bb9: {
58+
// StorageLive(_7);
59+
// _7 = (_1.0: u32);
60+
// StorageLive(_8);
61+
// _8 = (_1.3: u32);
62+
// StorageLive(_9);
63+
// _9 = _7;
64+
// StorageLive(_10);
65+
// StorageLive(_11);
66+
// _11 = _8;
67+
// _10 = Mul(const 2u32, move _11);
68+
// StorageDead(_11);
69+
// _0 = Add(move _9, move _10);
70+
// StorageDead(_10);
71+
// StorageDead(_9);
72+
// StorageDead(_8);
73+
// StorageDead(_7);
74+
// goto -> bb10;
75+
// }
76+
// bb10: {
77+
// return;
78+
// }
79+
// END rustc.match_tuple.SimplifyCfg-initial.after.mir

src/test/mir-opt/issue-62289.rs

+19-19
Original file line numberDiff line numberDiff line change
@@ -32,47 +32,47 @@ fn main() {
3232
// bb2: {
3333
// StorageDead(_4);
3434
// _5 = discriminant(_3);
35-
// switchInt(move _5) -> [0isize: bb10, 1isize: bb5, otherwise: bb4];
35+
// switchInt(move _5) -> [0isize: bb4, 1isize: bb6, otherwise: bb5];
3636
// }
3737
// bb3 (cleanup): {
3838
// drop(_2) -> bb1;
3939
// }
4040
// bb4: {
41-
// unreachable;
41+
// StorageLive(_10);
42+
// _10 = ((_3 as Ok).0: u32);
43+
// (*_2) = _10;
44+
// StorageDead(_10);
45+
// _1 = move _2;
46+
// drop(_2) -> [return: bb12, unwind: bb11];
4247
// }
4348
// bb5: {
49+
// unreachable;
50+
// }
51+
// bb6: {
4452
// StorageLive(_6);
4553
// _6 = ((_3 as Err).0: std::option::NoneError);
4654
// StorageLive(_8);
4755
// StorageLive(_9);
4856
// _9 = _6;
49-
// _8 = const <std::option::NoneError as std::convert::From<std::option::NoneError>>::from(move _9) -> [return: bb7, unwind: bb3];
57+
// _8 = const <std::option::NoneError as std::convert::From<std::option::NoneError>>::from(move _9) -> [return: bb8, unwind: bb3];
5058
// }
51-
// bb6: {
59+
// bb7: {
5260
// return;
5361
// }
54-
// bb7: {
62+
// bb8: {
5563
// StorageDead(_9);
56-
// _0 = const <std::option::Option<std::boxed::Box<u32>> as std::ops::Try>::from_error(move _8) -> [return: bb8, unwind: bb3];
64+
// _0 = const <std::option::Option<std::boxed::Box<u32>> as std::ops::Try>::from_error(move _8) -> [return: bb9, unwind: bb3];
5765
// }
58-
// bb8: {
66+
// bb9: {
5967
// StorageDead(_8);
6068
// StorageDead(_6);
61-
// drop(_2) -> bb9;
69+
// drop(_2) -> bb10;
6270
// }
63-
// bb9: {
71+
// bb10: {
6472
// StorageDead(_2);
6573
// StorageDead(_1);
6674
// StorageDead(_3);
67-
// goto -> bb6;
68-
// }
69-
// bb10: {
70-
// StorageLive(_10);
71-
// _10 = ((_3 as Ok).0: u32);
72-
// (*_2) = _10;
73-
// StorageDead(_10);
74-
// _1 = move _2;
75-
// drop(_2) -> [return: bb12, unwind: bb11];
75+
// goto -> bb7;
7676
// }
7777
// bb11 (cleanup): {
7878
// drop(_1) -> bb1;
@@ -85,7 +85,7 @@ fn main() {
8585
// bb13: {
8686
// StorageDead(_1);
8787
// StorageDead(_3);
88-
// goto -> bb6;
88+
// goto -> bb7;
8989
// }
9090
// }
9191
// END rustc.test.ElaborateDrops.before.mir

0 commit comments

Comments
 (0)