Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Initial implementation of or-pattern handling in MIR #63688

Closed
wants to merge 2 commits into from
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
41 changes: 39 additions & 2 deletions src/librustc_mir/build/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use syntax::ast::Name;
use syntax_pos::Span;

use std::mem;

// helper functions, broken out by category:
mod simplify;
mod test;
Expand Down Expand Up @@ -665,7 +667,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

#[derive(Debug)]
#[derive(Clone, Debug)]
pub struct Candidate<'pat, 'tcx> {
// span of the original pattern that gave rise to this candidate
span: Span,
Expand Down Expand Up @@ -883,7 +885,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}

// Test for the remaining candidates.
self.test_candidates(
self.test_candidates_with_or(
span,
unmatched_candidates,
block,
Expand Down Expand Up @@ -1026,6 +1028,41 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}

fn test_candidates_with_or<'pat, 'b, 'c>(
&mut self,
span: Span,
candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
block: BasicBlock,
otherwise_block: Option<BasicBlock>,
fake_borrows: &mut Option<FxHashSet<Place<'tcx>>>,
) {
let match_pair = &candidates.first().unwrap().match_pairs[0];
// FIXME(dlrobertson): This could use some cleaning up.
if let PatKind::Or { ref pats } = *match_pair.pattern.kind {
let match_pairs = mem::take(&mut candidates.first_mut().unwrap().match_pairs);
let mut new_candidates = pats.iter().map(|pat| {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was expecting these to be an entirely new set of candidates, containing only the match pairs that come from the or pattern. So this function would call match_candidates 3 times: once for the first candidate, once for the remaining candidates and once for the new candidates from the or pattern.

let mut candidate = (*candidates.first().unwrap()).clone();
candidate.match_pairs = match_pairs.clone();
candidate.match_pairs[0].pattern = pat;
candidate
}).collect::<Vec<_>>();
let mut new_candidate_refs = new_candidates.iter_mut().collect::<Vec<_>>();
for candidate in candidates.iter_mut().skip(1) {
new_candidate_refs.push(candidate);
}
self.test_candidates(span, &mut *new_candidate_refs, block,
otherwise_block, fake_borrows);
for candidate in new_candidates.iter_mut() {
if !candidate.match_pairs.is_empty() {
candidate.match_pairs.remove(0);
}
}
} else {
self.test_candidates(span, candidates, block,
otherwise_block, fake_borrows)
}
}

/// This is the most subtle part of the matching algorithm. At
/// this point, the input candidates have been fully simplified,
/// and so we know that all remaining match-pairs require some
Expand Down
20 changes: 16 additions & 4 deletions src/librustc_mir/hair/pattern/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1480,8 +1480,14 @@ fn pat_constructors<'tcx>(
Some(vec![Slice(pat_len)])
}
}
PatKind::Or { .. } => {
bug!("support for or-patterns has not been fully implemented yet.");
PatKind::Or { ref pats } => {
let mut v = vec![];
for pat in pats {
if let Some(ctors) = pat_constructors(cx, pat, pcx) {
v.extend(ctors);
}
}
Some(v)
}
}
}
Expand Down Expand Up @@ -2053,8 +2059,14 @@ fn specialize<'p, 'a: 'p, 'tcx>(
}
}

PatKind::Or { .. } => {
bug!("support for or-patterns has not been fully implemented yet.");
PatKind::Or { ref pats } => {
let mut specialized_pats = vec![];
for pat in pats {
if let Some(s) = specialize(cx, &[pat], constructor, wild_patterns) {
specialized_pats.extend(s);
}
}
Some(SmallVec::from_vec(specialized_pats))
}
};
debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head);
Expand Down
32 changes: 32 additions & 0 deletions src/test/ui/or-patterns/basic-switch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Test basic or-patterns when the target pattern type will be lowered to a
// `SwitchInt` (an `enum`).
// run-pass
#![feature(or_patterns)]
//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you allow this warning for the tests here?


#[derive(Debug)]
enum Test {
Foo,
Bar,
Baz,
Qux
}

fn test(x: Option<Test>) -> bool {
match x {
// most simple case
Some(Test::Bar | Test::Qux) => true,
// wild case
Some(_) => false,
// empty case
None => false,
}
}

fn main() {
assert!(!test(Some(Test::Foo)));
assert!(test(Some(Test::Bar)));
assert!(!test(Some(Test::Baz)));
assert!(test(Some(Test::Qux)));
assert!(!test(None))
}
8 changes: 8 additions & 0 deletions src/test/ui/or-patterns/basic-switch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
--> $DIR/basic-switch.rs:4:12
|
LL | #![feature(or_patterns)]
| ^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

56 changes: 56 additions & 0 deletions src/test/ui/or-patterns/basic-switchint.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Test basic or-patterns when the target pattern type will be lowered to
// a `Switch`. This will happen when the target type is an integer.
// run-pass
#![feature(or_patterns)]
//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash

#[derive(Debug, PartialEq)]
enum MatchArm {
Arm(usize),
Wild
}

#[derive(Debug)]
enum Foo {
One(usize),
Two(usize, usize),
}

fn test_foo(x: Foo) -> MatchArm {
match x {
// normal pattern.
Foo::One(0) | Foo::One(1) | Foo::One(2) => MatchArm::Arm(0),
// most simple or-pattern.
Foo::One(42 | 255) => MatchArm::Arm(1),
// multiple or-patterns for one structure.
Foo::Two(42 | 255, 1024 | 2048) => MatchArm::Arm(2),
// mix of pattern types in one or-pattern (range).
//
// FIXME(dlrobertson | Nadrieril): Fix or-pattern completeness and
// unreachabilitychecks for ranges.
Foo::One(100 | 110..=120 | 210..=220) => MatchArm::Arm(3),
// multiple or-patterns with wild.
Foo::Two(0..=10 | 100..=110, 0 | _) => MatchArm::Arm(4),
// wild
_ => MatchArm::Wild
}
}

fn main() {
// `Foo` tests.
assert_eq!(test_foo(Foo::One(0)), MatchArm::Arm(0));
assert_eq!(test_foo(Foo::One(42)), MatchArm::Arm(1));
assert_eq!(test_foo(Foo::One(43)), MatchArm::Wild);
assert_eq!(test_foo(Foo::One(255)), MatchArm::Arm(1));
assert_eq!(test_foo(Foo::One(256)), MatchArm::Wild);
assert_eq!(test_foo(Foo::Two(42, 1023)), MatchArm::Wild);
assert_eq!(test_foo(Foo::Two(255, 2048)), MatchArm::Arm(2));
assert_eq!(test_foo(Foo::One(100)), MatchArm::Arm(3));
assert_eq!(test_foo(Foo::One(115)), MatchArm::Arm(3));
assert_eq!(test_foo(Foo::One(105)), MatchArm::Wild);
assert_eq!(test_foo(Foo::One(215)), MatchArm::Arm(3));
assert_eq!(test_foo(Foo::One(121)), MatchArm::Wild);
assert_eq!(test_foo(Foo::Two(0, 42)), MatchArm::Arm(4));
assert_eq!(test_foo(Foo::Two(100, 0)), MatchArm::Arm(4));
assert_eq!(test_foo(Foo::Two(42, 0)), MatchArm::Wild);
}
22 changes: 22 additions & 0 deletions src/test/ui/or-patterns/basic-switchint.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
--> $DIR/basic-switchint.rs:4:12
|
LL | #![feature(or_patterns)]
| ^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

warning: unreachable pattern
--> $DIR/basic-switchint.rs:31:9
|
LL | Foo::One(100 | 110..=120 | 210..=220) => MatchArm::Arm(3),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(unreachable_patterns)]` on by default

warning: unreachable pattern
--> $DIR/basic-switchint.rs:33:9
|
LL | Foo::Two(0..=10 | 100..=110, 0 | _) => MatchArm::Arm(4),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

20 changes: 20 additions & 0 deletions src/test/ui/or-patterns/mix-with-wild.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Test that an or-pattern works with a wild pattern. This tests two things:
//
// 1) The Wild pattern should cause the pattern to always succeed.
// 2) or-patterns should work with simplifyable patterns.
//
// run-pass
#![feature(or_patterns)]
//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash

pub fn test(x: Option<usize>) -> bool {
match x {
Some(0 | _) => true,
_ => false
}
}

fn main() {
assert!(test(Some(42)));
assert!(!test(None));
}
8 changes: 8 additions & 0 deletions src/test/ui/or-patterns/mix-with-wild.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
--> $DIR/mix-with-wild.rs:7:12
|
LL | #![feature(or_patterns)]
| ^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default

48 changes: 48 additions & 0 deletions src/test/ui/or-patterns/struct-like.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// run-pass
#![feature(or_patterns)]
//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash

#[derive(Debug)]
enum Other {
One,
Two,
Three,
}

#[derive(Debug)]
enum Test {
Foo { first: usize, second: usize },
Bar { other: Option<Other> },
Baz,
}

fn test(x: Option<Test>) -> bool {
match x {
Some(
Test::Foo {
first: 1024 | 2048,
second: 2048 | 4096
} |
Test::Bar { other: Some(
Other::One |
Other::Two
)}
) => true,
// wild case
Some(_) => false,
// empty case
None => false,
}
}

fn main() {
assert!(test(Some(Test::Foo { first: 1024, second: 4096 })));
assert!(!test(Some(Test::Foo { first: 2048, second: 8192 })));
assert!(!test(Some(Test::Foo { first: 42, second: 2048 })));
assert!(test(Some(Test::Bar { other: Some(Other::One) })));
assert!(test(Some(Test::Bar { other: Some(Other::Two) })));
assert!(!test(Some(Test::Bar { other: Some(Other::Three) })));
assert!(!test(Some(Test::Bar { other: None })));
assert!(!test(Some(Test::Baz)));
assert!(!test(None));
}
8 changes: 8 additions & 0 deletions src/test/ui/or-patterns/struct-like.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
warning: the feature `or_patterns` is incomplete and may cause the compiler to crash
--> $DIR/struct-like.rs:2:12
|
LL | #![feature(or_patterns)]
| ^^^^^^^^^^^
|
= note: `#[warn(incomplete_features)]` on by default