Skip to content

Commit 42a0bd2

Browse files
committed
Auto merge of #67668 - matthewjasper:or-patterns, r=pnkfelix
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 8417d68 + 8dbbe4d commit 42a0bd2

File tree

65 files changed

+1737
-894
lines changed

Some content is hidden

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

65 files changed

+1737
-894
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
sym::const_trait_impl,

src/librustc_mir_build/build/block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
149149
&pattern,
150150
UserTypeProjections::none(),
151151
&mut |this, _, _, _, node, span, _, _| {
152-
this.storage_live_binding(block, node, span, OutsideGuard);
152+
this.storage_live_binding(block, node, span, OutsideGuard, true);
153153
this.schedule_drop_for_binding(node, span, OutsideGuard);
154154
},
155155
)

src/librustc_mir_build/build/matches/mod.rs

+588-262
Large diffs are not rendered by default.

src/librustc_mir_build/build/matches/simplify.rs

+50-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::build::matches::{Ascription, Binding, Candidate, MatchPair};
1616
use crate::build::Builder;
1717
use crate::hair::{self, *};
1818
use rustc::mir::interpret::truncate;
19+
use rustc::mir::Place;
1920
use rustc::ty;
2021
use rustc::ty::layout::{Integer, IntegerExt, Size};
2122
use rustc_attr::{SignedInt, UnsignedInt};
@@ -24,10 +25,33 @@ use rustc_hir::RangeEnd;
2425
use std::mem;
2526

2627
impl<'a, 'tcx> Builder<'a, 'tcx> {
27-
crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
28+
/// Simplify a candidate so that all match pairs require a test.
29+
///
30+
/// This method will also split a candidate where the only match-pair is an
31+
/// or-pattern into multiple candidates. This is so that
32+
///
33+
/// match x {
34+
/// 0 | 1 => { ... },
35+
/// 2 | 3 => { ... },
36+
/// }
37+
///
38+
/// only generates a single switch. If this happens this method returns
39+
/// `true`.
40+
pub(super) fn simplify_candidate<'pat>(
41+
&mut self,
42+
candidate: &mut Candidate<'pat, 'tcx>,
43+
) -> bool {
2844
// repeatedly simplify match pairs until fixed point is reached
2945
loop {
3046
let match_pairs = mem::take(&mut candidate.match_pairs);
47+
48+
if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] =
49+
*match_pairs
50+
{
51+
candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
52+
return true;
53+
}
54+
3155
let mut changed = false;
3256
for match_pair in match_pairs {
3357
match self.simplify_match_pair(match_pair, candidate) {
@@ -40,11 +64,35 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
4064
}
4165
}
4266
if !changed {
43-
return; // if we were not able to simplify any, done.
67+
// Move or-patterns to the end, because they can result in us
68+
// creating additional candidates, so we want to test them as
69+
// late as possible.
70+
candidate
71+
.match_pairs
72+
.sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
73+
return false; // if we were not able to simplify any, done.
4474
}
4575
}
4676
}
4777

78+
/// Given `candidate` that has a single or-pattern for its match-pairs,
79+
/// creates a fresh candidate for each of its input subpatterns passed via
80+
/// `pats`.
81+
fn create_or_subcandidates<'pat>(
82+
&mut self,
83+
candidate: &Candidate<'pat, 'tcx>,
84+
place: Place<'tcx>,
85+
pats: &'pat [Pat<'tcx>],
86+
) -> Vec<Candidate<'pat, 'tcx>> {
87+
pats.iter()
88+
.map(|pat| {
89+
let mut candidate = Candidate::new(place, pat, candidate.has_guard);
90+
self.simplify_candidate(&mut candidate);
91+
candidate
92+
})
93+
.collect()
94+
}
95+
4896
/// Tries to simplify `match_pair`, returning `Ok(())` if
4997
/// successful. If successful, new match pairs and bindings will
5098
/// have been pushed into the candidate. If no simplification is

src/librustc_mir_build/build/matches/test.rs

+16-26
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
2424
/// Identifies what test is needed to decide if `match_pair` is applicable.
2525
///
2626
/// It is a bug to call this with a simplifiable pattern.
27-
crate fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
27+
pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
2828
match *match_pair.pattern.kind {
2929
PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test {
3030
span: match_pair.pattern.span,
@@ -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 { .. }
@@ -85,7 +81,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
8581
}
8682
}
8783

88-
crate fn add_cases_to_switch<'pat>(
84+
pub(super) fn add_cases_to_switch<'pat>(
8985
&mut self,
9086
test_place: &Place<'tcx>,
9187
candidate: &Candidate<'pat, 'tcx>,
@@ -129,7 +125,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
129125
}
130126
}
131127

132-
crate fn add_variants_to_switch<'pat>(
128+
pub(super) fn add_variants_to_switch<'pat>(
133129
&mut self,
134130
test_place: &Place<'tcx>,
135131
candidate: &Candidate<'pat, 'tcx>,
@@ -156,10 +152,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
156152
}
157153
}
158154

159-
crate fn perform_test(
155+
pub(super) fn perform_test(
160156
&mut self,
161157
block: BasicBlock,
162-
place: &Place<'tcx>,
158+
place: Place<'tcx>,
163159
test: &Test<'tcx>,
164160
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
165161
) {
@@ -209,7 +205,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
209205
);
210206
let discr_ty = adt_def.repr.discr_type().to_ty(tcx);
211207
let discr = self.temp(discr_ty, test.span);
212-
self.cfg.push_assign(block, source_info, &discr, Rvalue::Discriminant(*place));
208+
self.cfg.push_assign(block, source_info, &discr, Rvalue::Discriminant(place));
213209
assert_eq!(values.len() + 1, targets.len());
214210
self.cfg.terminate(
215211
block,
@@ -233,20 +229,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
233229
0 => (second_bb, first_bb),
234230
v => span_bug!(test.span, "expected boolean value but got {:?}", v),
235231
};
236-
TerminatorKind::if_(
237-
self.hir.tcx(),
238-
Operand::Copy(*place),
239-
true_bb,
240-
false_bb,
241-
)
232+
TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place), true_bb, false_bb)
242233
} else {
243234
bug!("`TestKind::SwitchInt` on `bool` should have two targets")
244235
}
245236
} else {
246237
// The switch may be inexhaustive so we have a catch all block
247238
debug_assert_eq!(options.len() + 1, target_blocks.len());
248239
TerminatorKind::SwitchInt {
249-
discr: Operand::Copy(*place),
240+
discr: Operand::Copy(place),
250241
switch_ty,
251242
values: options.clone().into(),
252243
targets: target_blocks,
@@ -271,7 +262,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
271262
if let [success, fail] = *make_target_blocks(self) {
272263
assert_eq!(value.ty, ty);
273264
let expect = self.literal_operand(test.span, value);
274-
let val = Operand::Copy(*place);
265+
let val = Operand::Copy(place);
275266
self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
276267
} else {
277268
bug!("`TestKind::Eq` should have two target blocks");
@@ -286,7 +277,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
286277
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
287278
let lo = self.literal_operand(test.span, lo);
288279
let hi = self.literal_operand(test.span, hi);
289-
let val = Operand::Copy(*place);
280+
let val = Operand::Copy(place);
290281

291282
if let [success, fail] = *target_blocks {
292283
self.compare(
@@ -315,7 +306,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
315306
let actual = self.temp(usize_ty, test.span);
316307

317308
// actual = len(place)
318-
self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(*place));
309+
self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(place));
319310

320311
// expected = <N>
321312
let expected = self.push_usize(block, source_info, len);
@@ -371,13 +362,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
371362
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
372363
source_info: SourceInfo,
373364
value: &'tcx ty::Const<'tcx>,
374-
place: &Place<'tcx>,
365+
place: Place<'tcx>,
375366
mut ty: Ty<'tcx>,
376367
) {
377368
use rustc::middle::lang_items::EqTraitLangItem;
378369

379370
let mut expect = self.literal_operand(source_info.span, value);
380-
let mut val = Operand::Copy(*place);
371+
let mut val = Operand::Copy(place);
381372

382373
// If we're using `b"..."` as a pattern, we need to insert an
383374
// unsizing coercion, as the byte string has the type `&[u8; N]`.
@@ -502,7 +493,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
502493
/// that it *doesn't* apply. For now, we return false, indicate that the
503494
/// test does not apply to this candidate, but it might be we can get
504495
/// tighter match code if we do something a bit different.
505-
crate fn sort_candidate<'pat>(
496+
pub(super) fn sort_candidate<'pat>(
506497
&mut self,
507498
test_place: &Place<'tcx>,
508499
test: &Test<'tcx>,
@@ -755,8 +746,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
755746
let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)`
756747
let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
757748
// e.g., `(x as Variant).0`
758-
let place =
759-
tcx.mk_place_field(downcast_place.clone(), subpattern.field, subpattern.pattern.ty);
749+
let place = tcx.mk_place_field(downcast_place, subpattern.field, subpattern.pattern.ty);
760750
// e.g., `(x as Variant).0 @ P1`
761751
MatchPair::new(place, &subpattern.pattern)
762752
});

src/librustc_mir_build/build/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -899,7 +899,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
899899
matches::ArmHasGuard(false),
900900
Some((Some(&place), span)),
901901
);
902-
unpack!(block = self.place_into_pattern(block, pattern, &place, false));
902+
unpack!(block = self.place_into_pattern(block, pattern, place, false));
903903
}
904904
}
905905
self.source_scope = original_source_scope;

src/librustc_mir_build/hair/mod.rs

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

318-
impl<'tcx> Arm<'tcx> {
319-
// HACK(or_patterns; Centril | dlrobertson): Remove this and
320-
// correctly handle each case in which this method is used.
321-
crate 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
crate 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

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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 ^ 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+
// _10 = _8;
66+
// _0 = BitXor(move _9, move _10);
67+
// StorageDead(_10);
68+
// StorageDead(_9);
69+
// StorageDead(_8);
70+
// StorageDead(_7);
71+
// goto -> bb10;
72+
// }
73+
// bb10: {
74+
// return;
75+
// }
76+
// END rustc.match_tuple.SimplifyCfg-initial.after.mir

0 commit comments

Comments
 (0)