Skip to content

Commit

Permalink
Auto merge of #67668 - matthewjasper:or-patterns, r=<try>
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed Dec 28, 2019
2 parents 2ee25da + 232d661 commit 844409e
Show file tree
Hide file tree
Showing 61 changed files with 1,535 additions and 836 deletions.
1 change: 0 additions & 1 deletion src/librustc_feature/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,6 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
sym::impl_trait_in_bindings,
sym::generic_associated_types,
sym::const_generics,
sym::or_patterns,
sym::let_chains,
sym::raw_dylib,
];
693 changes: 463 additions & 230 deletions src/librustc_mir/build/matches/mod.rs

Large diffs are not rendered by default.

58 changes: 56 additions & 2 deletions src/librustc_mir/build/matches/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,39 @@ use crate::build::Builder;
use crate::hair::{self, *};
use rustc::hir::RangeEnd;
use rustc::mir::interpret::truncate;
use rustc::mir::Place;
use rustc::ty;
use rustc::ty::layout::{Integer, IntegerExt, Size};
use syntax::attr::{SignedInt, UnsignedInt};

use smallvec::smallvec;
use std::mem;

impl<'a, 'tcx> Builder<'a, 'tcx> {
pub fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) {
/// Simplify a candidate so that all match pairs require a test.
///
/// This method will also split a candidate where the only match-pair is an
/// or-pattern into multiple candidates. This is so that
///
/// match x {
/// 0 | 1 => { ... },
/// 2 | 3 => { ... },
/// }
///
/// only generates a single switch. If this happens this method returns
/// `true`.
pub fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) -> bool {
// repeatedly simplify match pairs until fixed point is reached
loop {
let match_pairs = mem::take(&mut candidate.match_pairs);

if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, ref place }] =
*match_pairs
{
candidate.subcandidates = self.create_or_subcanidates(candidate, place, pats);
return true;
}

let mut changed = false;
for match_pair in match_pairs {
match self.simplify_match_pair(match_pair, candidate) {
Expand All @@ -40,11 +62,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
if !changed {
return; // if we were not able to simplify any, done.
// Move or-patterns to the end, because they can result in us
// creating additional candidates, so we want to test them as
// late as possible.
candidate
.match_pairs
.sort_by_key(|pair| matches!(*pair.pattern.kind, PatKind::Or { .. }));
return false; // if we were not able to simplify any, done.
}
}
}

fn create_or_subcanidates<'pat>(
&mut self,
candidate: &Candidate<'pat, 'tcx>,
place: &Place<'tcx>,
pats: &'pat [Pat<'tcx>],
) -> Vec<Candidate<'pat, 'tcx>> {
pats.iter()
.map(|pat| {
let mut candidate = Candidate {
span: pat.span,
has_guard: candidate.has_guard,
needs_otherwise_block: candidate.needs_otherwise_block,
match_pairs: smallvec![MatchPair { place: place.clone(), pattern: pat }],
bindings: vec![],
ascriptions: vec![],
subcandidates: vec![],
otherwise_block: None,
pre_binding_block: None,
next_candidate_pre_binding_block: None,
};
self.simplify_candidate(&mut candidate);
candidate
})
.collect()
}

/// Tries to simplify `match_pair`, returning `Ok(())` if
/// successful. If successful, new match pairs and bindings will
/// have been pushed into the candidate. If no simplification is
Expand Down
6 changes: 1 addition & 5 deletions src/librustc_mir/build/matches/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

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

PatKind::AscribeUserType { .. }
| PatKind::Array { .. }
Expand Down
11 changes: 0 additions & 11 deletions src/librustc_mir/hair/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,17 +315,6 @@ pub struct Arm<'tcx> {
pub span: Span,
}

impl Arm<'tcx> {
// HACK(or_patterns; Centril | dlrobertson): Remove this and
// correctly handle each case in which this method is used.
pub fn top_pats_hack(&self) -> &[Pat<'tcx>] {
match &*self.pattern.kind {
PatKind::Or { pats } => pats,
_ => std::slice::from_ref(&self.pattern),
}
}
}

#[derive(Clone, Debug)]
pub enum Guard<'tcx> {
If(ExprRef<'tcx>),
Expand Down
20 changes: 10 additions & 10 deletions src/test/mir-opt/const_prop/discriminant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ fn main() {
// ...
// _3 = std::option::Option::<bool>::Some(const true,);
// _4 = discriminant(_3);
// switchInt(move _4) -> [1isize: bb3, otherwise: bb2];
// switchInt(move _4) -> [1isize: bb2, otherwise: bb1];
// }
// bb1: {
// _2 = const 42i32;
// _2 = const 10i32;
// goto -> bb4;
// }
// bb2: {
// _2 = const 10i32;
// goto -> bb4;
// switchInt(((_3 as Some).0: bool)) -> [false: bb1, otherwise: bb3];
// }
// bb3: {
// switchInt(((_3 as Some).0: bool)) -> [false: bb2, otherwise: bb1];
// _2 = const 42i32;
// goto -> bb4;
// }
// bb4: {
// _1 = Add(move _2, const 0i32);
Expand All @@ -33,18 +33,18 @@ fn main() {
// ...
// _3 = const Scalar(0x01) : std::option::Option<bool>;
// _4 = const 1isize;
// switchInt(const 1isize) -> [1isize: bb3, otherwise: bb2];
// switchInt(const 1isize) -> [1isize: bb2, otherwise: bb1];
// }
// bb1: {
// _2 = const 42i32;
// _2 = const 10i32;
// goto -> bb4;
// }
// bb2: {
// _2 = const 10i32;
// goto -> bb4;
// switchInt(const true) -> [false: bb1, otherwise: bb3];
// }
// bb3: {
// switchInt(const true) -> [false: bb2, otherwise: bb1];
// _2 = const 42i32;
// goto -> bb4;
// }
// bb4: {
// _1 = Add(move _2, const 0i32);
Expand Down
79 changes: 79 additions & 0 deletions src/test/mir-opt/exponential-or.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Test that simple or-patterns don't get expanded to exponentially large CFGs

// ignore-tidy-linelength

#![feature(or_patterns)]

fn match_tuple(x: (u32, bool, Option<i32>, u32)) -> u32 {
match x {
(y @ (1 | 4), true | false, Some(1 | 8) | None, z @ (6..=9 | 13..=16)) => y + 2 * z,
_ => 0,
}
}

fn main() {}

// END RUST SOURCE

// START rustc.match_tuple.SimplifyCfg-initial.after.mir
// scope 1 {
// debug y => _7;
// debug z => _8;
// }
// bb0: {
// FakeRead(ForMatchedPlace, _1);
// switchInt((_1.0: u32)) -> [1u32: bb2, 4u32: bb2, otherwise: bb1];
// }
// bb1: {
// _0 = const 0u32;
// goto -> bb10;
// }
// bb2: {
// _2 = discriminant((_1.2: std::option::Option<i32>));
// switchInt(move _2) -> [0isize: bb4, 1isize: bb3, otherwise: bb1];
// }
// bb3: {
// switchInt((((_1.2: std::option::Option<i32>) as Some).0: i32)) -> [1i32: bb4, 8i32: bb4, otherwise: bb1];
// }
// bb4: {
// _5 = Le(const 6u32, (_1.3: u32));
// switchInt(move _5) -> [false: bb6, otherwise: bb5];
// }
// bb5: {
// _6 = Le((_1.3: u32), const 9u32);
// switchInt(move _6) -> [false: bb6, otherwise: bb8];
// }
// bb6: {
// _3 = Le(const 13u32, (_1.3: u32));
// switchInt(move _3) -> [false: bb1, otherwise: bb7];
// }
// bb7: {
// _4 = Le((_1.3: u32), const 16u32);
// switchInt(move _4) -> [false: bb1, otherwise: bb8];
// }
// bb8: {
// falseEdges -> [real: bb9, imaginary: bb1];
// }
// bb9: {
// StorageLive(_7);
// _7 = (_1.0: u32);
// StorageLive(_8);
// _8 = (_1.3: u32);
// StorageLive(_9);
// _9 = _7;
// StorageLive(_10);
// StorageLive(_11);
// _11 = _8;
// _10 = Mul(const 2u32, move _11);
// StorageDead(_11);
// _0 = Add(move _9, move _10);
// StorageDead(_10);
// StorageDead(_9);
// StorageDead(_8);
// StorageDead(_7);
// goto -> bb10;
// }
// bb10: {
// return;
// }
// END rustc.match_tuple.SimplifyCfg-initial.after.mir
38 changes: 19 additions & 19 deletions src/test/mir-opt/issue-62289.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,47 +32,47 @@ fn main() {
// bb2: {
// StorageDead(_4);
// _5 = discriminant(_3);
// switchInt(move _5) -> [0isize: bb10, 1isize: bb5, otherwise: bb4];
// switchInt(move _5) -> [0isize: bb4, 1isize: bb6, otherwise: bb5];
// }
// bb3 (cleanup): {
// drop(_2) -> bb1;
// }
// bb4: {
// unreachable;
// StorageLive(_10);
// _10 = ((_3 as Ok).0: u32);
// (*_2) = _10;
// StorageDead(_10);
// _1 = move _2;
// drop(_2) -> [return: bb12, unwind: bb11];
// }
// bb5: {
// unreachable;
// }
// bb6: {
// StorageLive(_6);
// _6 = ((_3 as Err).0: std::option::NoneError);
// StorageLive(_8);
// StorageLive(_9);
// _9 = _6;
// _8 = const <std::option::NoneError as std::convert::From<std::option::NoneError>>::from(move _9) -> [return: bb7, unwind: bb3];
// _8 = const <std::option::NoneError as std::convert::From<std::option::NoneError>>::from(move _9) -> [return: bb8, unwind: bb3];
// }
// bb6: {
// bb7: {
// return;
// }
// bb7: {
// bb8: {
// StorageDead(_9);
// _0 = const <std::option::Option<std::boxed::Box<u32>> as std::ops::Try>::from_error(move _8) -> [return: bb8, unwind: bb3];
// _0 = const <std::option::Option<std::boxed::Box<u32>> as std::ops::Try>::from_error(move _8) -> [return: bb9, unwind: bb3];
// }
// bb8: {
// bb9: {
// StorageDead(_8);
// StorageDead(_6);
// drop(_2) -> bb9;
// drop(_2) -> bb10;
// }
// bb9: {
// bb10: {
// StorageDead(_2);
// StorageDead(_1);
// StorageDead(_3);
// goto -> bb6;
// }
// bb10: {
// StorageLive(_10);
// _10 = ((_3 as Ok).0: u32);
// (*_2) = _10;
// StorageDead(_10);
// _1 = move _2;
// drop(_2) -> [return: bb12, unwind: bb11];
// goto -> bb7;
// }
// bb11 (cleanup): {
// drop(_1) -> bb1;
Expand All @@ -85,7 +85,7 @@ fn main() {
// bb13: {
// StorageDead(_1);
// StorageDead(_3);
// goto -> bb6;
// goto -> bb7;
// }
// }
// END rustc.test.ElaborateDrops.before.mir
Loading

0 comments on commit 844409e

Please sign in to comment.