From 619fc1bb75afa27c456c231047d5a7e23a688a0e Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Sun, 27 Oct 2019 22:51:46 +0000 Subject: [PATCH 1/2] rustc_mir: match pair with or-pattern handling Handle or-patterns when building MIR by expanding the MatchPair into n Candidates. --- src/librustc_mir/build/matches/mod.rs | 41 +++++++++++++++++++++++-- src/librustc_mir/hair/pattern/_match.rs | 20 +++++++++--- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 667b37bbd80c8..9d56fbca0b15c 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -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; @@ -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, @@ -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, @@ -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, + fake_borrows: &mut Option>>, + ) { + 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| { + let mut candidate = (*candidates.first().unwrap()).clone(); + candidate.match_pairs = match_pairs.clone(); + candidate.match_pairs[0].pattern = pat; + candidate + }).collect::>(); + let mut new_candidate_refs = new_candidates.iter_mut().collect::>(); + 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 diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 907c84b6f8cf0..0f5a9d4632026 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -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) } } } @@ -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); From 1bf639a8cc604f23f8a88177fe5254d5952c4554 Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Sun, 14 Jul 2019 01:33:02 +0000 Subject: [PATCH 2/2] basic run-pass tests for or-patterns Add some basic run-pass ui tests for or-patterns. --- src/test/ui/or-patterns/basic-switch.rs | 32 +++++++++++ src/test/ui/or-patterns/basic-switch.stderr | 8 +++ src/test/ui/or-patterns/basic-switchint.rs | 56 +++++++++++++++++++ .../ui/or-patterns/basic-switchint.stderr | 22 ++++++++ src/test/ui/or-patterns/mix-with-wild.rs | 20 +++++++ src/test/ui/or-patterns/mix-with-wild.stderr | 8 +++ src/test/ui/or-patterns/struct-like.rs | 48 ++++++++++++++++ src/test/ui/or-patterns/struct-like.stderr | 8 +++ 8 files changed, 202 insertions(+) create mode 100644 src/test/ui/or-patterns/basic-switch.rs create mode 100644 src/test/ui/or-patterns/basic-switch.stderr create mode 100644 src/test/ui/or-patterns/basic-switchint.rs create mode 100644 src/test/ui/or-patterns/basic-switchint.stderr create mode 100644 src/test/ui/or-patterns/mix-with-wild.rs create mode 100644 src/test/ui/or-patterns/mix-with-wild.stderr create mode 100644 src/test/ui/or-patterns/struct-like.rs create mode 100644 src/test/ui/or-patterns/struct-like.stderr diff --git a/src/test/ui/or-patterns/basic-switch.rs b/src/test/ui/or-patterns/basic-switch.rs new file mode 100644 index 0000000000000..17c4489f59b82 --- /dev/null +++ b/src/test/ui/or-patterns/basic-switch.rs @@ -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 + +#[derive(Debug)] +enum Test { + Foo, + Bar, + Baz, + Qux +} + +fn test(x: Option) -> 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)) +} diff --git a/src/test/ui/or-patterns/basic-switch.stderr b/src/test/ui/or-patterns/basic-switch.stderr new file mode 100644 index 0000000000000..e6f4b33a1ade6 --- /dev/null +++ b/src/test/ui/or-patterns/basic-switch.stderr @@ -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 + diff --git a/src/test/ui/or-patterns/basic-switchint.rs b/src/test/ui/or-patterns/basic-switchint.rs new file mode 100644 index 0000000000000..663177a3af8d7 --- /dev/null +++ b/src/test/ui/or-patterns/basic-switchint.rs @@ -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); +} diff --git a/src/test/ui/or-patterns/basic-switchint.stderr b/src/test/ui/or-patterns/basic-switchint.stderr new file mode 100644 index 0000000000000..cf39aef78b385 --- /dev/null +++ b/src/test/ui/or-patterns/basic-switchint.stderr @@ -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), + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/src/test/ui/or-patterns/mix-with-wild.rs b/src/test/ui/or-patterns/mix-with-wild.rs new file mode 100644 index 0000000000000..68829f1356627 --- /dev/null +++ b/src/test/ui/or-patterns/mix-with-wild.rs @@ -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) -> bool { + match x { + Some(0 | _) => true, + _ => false + } +} + +fn main() { + assert!(test(Some(42))); + assert!(!test(None)); +} diff --git a/src/test/ui/or-patterns/mix-with-wild.stderr b/src/test/ui/or-patterns/mix-with-wild.stderr new file mode 100644 index 0000000000000..88d971a3f0e55 --- /dev/null +++ b/src/test/ui/or-patterns/mix-with-wild.stderr @@ -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 + diff --git a/src/test/ui/or-patterns/struct-like.rs b/src/test/ui/or-patterns/struct-like.rs new file mode 100644 index 0000000000000..470783529a107 --- /dev/null +++ b/src/test/ui/or-patterns/struct-like.rs @@ -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 }, + Baz, +} + +fn test(x: Option) -> 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)); +} diff --git a/src/test/ui/or-patterns/struct-like.stderr b/src/test/ui/or-patterns/struct-like.stderr new file mode 100644 index 0000000000000..911f715643635 --- /dev/null +++ b/src/test/ui/or-patterns/struct-like.stderr @@ -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 +