From 425e494fceb2f88bec344ef07d0f2db4c74dd2d1 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 12:03:03 +0000 Subject: [PATCH 01/13] Remove or_patterns from INCOMPLETE_FEATURES --- src/librustc_feature/active.rs | 1 - .../ui/lint/issue-54538-unused-parens-lint.rs | 1 - .../issue-54538-unused-parens-lint.stderr | 58 ++++++++---------- src/test/ui/or-patterns/already-bound-name.rs | 1 - .../ui/or-patterns/already-bound-name.stderr | 38 +++++------- .../ui/or-patterns/consistent-bindings.rs | 1 - .../ui/or-patterns/consistent-bindings.stderr | 10 +--- .../exhaustiveness-non-exhaustive.rs | 7 +-- .../exhaustiveness-non-exhaustive.stderr | 8 +-- .../ui/or-patterns/exhaustiveness-pass.rs | 11 ++-- .../ui/or-patterns/exhaustiveness-pass.stderr | 2 +- .../exhaustiveness-unreachable-pattern.rs | 22 +++---- .../exhaustiveness-unreachable-pattern.stderr | 36 +++++------ .../ui/or-patterns/feature-gate-const-fn.rs | 1 - .../or-patterns/feature-gate-const-fn.stderr | 12 ++-- src/test/ui/or-patterns/inconsistent-modes.rs | 2 - .../ui/or-patterns/inconsistent-modes.stderr | 26 +++----- src/test/ui/or-patterns/missing-bindings.rs | 2 - .../ui/or-patterns/missing-bindings.stderr | 60 ++++++++----------- .../ui/or-patterns/multiple-pattern-typo.rs | 1 - .../or-patterns/multiple-pattern-typo.stderr | 22 +++---- .../or-patterns/or-patterns-syntactic-fail.rs | 1 - .../or-patterns-syntactic-fail.stderr | 40 +++++-------- .../or-patterns/or-patterns-syntactic-pass.rs | 2 +- .../or-patterns-syntactic-pass.stderr | 8 --- .../pat-at-same-name-both.rs | 1 - .../pat-at-same-name-both.stderr | 28 ++++----- 27 files changed, 154 insertions(+), 248 deletions(-) delete mode 100644 src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs index f20a57ea61c42..0252d88e73889 100644 --- a/src/librustc_feature/active.rs +++ b/src/librustc_feature/active.rs @@ -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, sym::const_trait_impl, diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.rs b/src/test/ui/lint/issue-54538-unused-parens-lint.rs index 7dcbdd0863cbd..f3d2d1bb58d8f 100644 --- a/src/test/ui/lint/issue-54538-unused-parens-lint.rs +++ b/src/test/ui/lint/issue-54538-unused-parens-lint.rs @@ -1,7 +1,6 @@ #![feature(box_patterns, stmt_expr_attributes)] #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete #![allow(ellipsis_inclusive_range_patterns)] #![allow(unreachable_patterns)] diff --git a/src/test/ui/lint/issue-54538-unused-parens-lint.stderr b/src/test/ui/lint/issue-54538-unused-parens-lint.stderr index b6d532c31017c..b31ad95b191c9 100644 --- a/src/test/ui/lint/issue-54538-unused-parens-lint.stderr +++ b/src/test/ui/lint/issue-54538-unused-parens-lint.stderr @@ -1,157 +1,149 @@ -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/issue-54538-unused-parens-lint.rs:3:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:12:9 + --> $DIR/issue-54538-unused-parens-lint.rs:11:9 | LL | let (a) = 0; | ^^^ help: remove these parentheses | note: the lint level is defined here - --> $DIR/issue-54538-unused-parens-lint.rs:9:9 + --> $DIR/issue-54538-unused-parens-lint.rs:8:9 | LL | #![deny(unused_parens)] | ^^^^^^^^^^^^^ error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:13:9 + --> $DIR/issue-54538-unused-parens-lint.rs:12:9 | LL | for (a) in 0..1 {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:14:12 + --> $DIR/issue-54538-unused-parens-lint.rs:13:12 | LL | if let (a) = 0 {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:15:15 + --> $DIR/issue-54538-unused-parens-lint.rs:14:15 | LL | while let (a) = 0 {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:16:12 + --> $DIR/issue-54538-unused-parens-lint.rs:15:12 | LL | fn foo((a): u8) {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:17:14 + --> $DIR/issue-54538-unused-parens-lint.rs:16:14 | LL | let _ = |(a): u8| 0; | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:45:12 + --> $DIR/issue-54538-unused-parens-lint.rs:44:12 | LL | if let (0 | 1) = 0 {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:46:13 + --> $DIR/issue-54538-unused-parens-lint.rs:45:13 | LL | if let ((0 | 1),) = (0,) {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:47:13 + --> $DIR/issue-54538-unused-parens-lint.rs:46:13 | LL | if let [(0 | 1)] = [0] {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:48:16 + --> $DIR/issue-54538-unused-parens-lint.rs:47:16 | LL | if let 0 | (1 | 2) = 0 {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:50:15 + --> $DIR/issue-54538-unused-parens-lint.rs:49:15 | LL | if let TS((0 | 1)) = TS(0) {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:52:20 + --> $DIR/issue-54538-unused-parens-lint.rs:51:20 | LL | if let NS { f: (0 | 1) } = (NS { f: 0 }) {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:62:9 + --> $DIR/issue-54538-unused-parens-lint.rs:61:9 | LL | (_) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:63:9 + --> $DIR/issue-54538-unused-parens-lint.rs:62:9 | LL | (y) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:64:9 + --> $DIR/issue-54538-unused-parens-lint.rs:63:9 | LL | (ref r) => {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:65:9 + --> $DIR/issue-54538-unused-parens-lint.rs:64:9 | LL | (e @ 1...2) => {} | ^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:71:9 + --> $DIR/issue-54538-unused-parens-lint.rs:70:9 | LL | (e @ &(1...2)) => {} | ^^^^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:72:10 + --> $DIR/issue-54538-unused-parens-lint.rs:71:10 | LL | &(_) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:83:9 + --> $DIR/issue-54538-unused-parens-lint.rs:82:9 | LL | (_) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:84:9 + --> $DIR/issue-54538-unused-parens-lint.rs:83:9 | LL | (y) => {} | ^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:85:9 + --> $DIR/issue-54538-unused-parens-lint.rs:84:9 | LL | (ref r) => {} | ^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:86:9 + --> $DIR/issue-54538-unused-parens-lint.rs:85:9 | LL | (e @ 1..=2) => {} | ^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:92:9 + --> $DIR/issue-54538-unused-parens-lint.rs:91:9 | LL | (e @ &(1..=2)) => {} | ^^^^^^^^^^^^^^ help: remove these parentheses error: unnecessary parentheses around pattern - --> $DIR/issue-54538-unused-parens-lint.rs:93:10 + --> $DIR/issue-54538-unused-parens-lint.rs:92:10 | LL | &(_) => {} | ^^^ help: remove these parentheses diff --git a/src/test/ui/or-patterns/already-bound-name.rs b/src/test/ui/or-patterns/already-bound-name.rs index 3ebf59c643735..726e17b7ec226 100644 --- a/src/test/ui/or-patterns/already-bound-name.rs +++ b/src/test/ui/or-patterns/already-bound-name.rs @@ -2,7 +2,6 @@ // correctly accounts for or-patterns. #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete enum E { A(T, T), B(T) } diff --git a/src/test/ui/or-patterns/already-bound-name.stderr b/src/test/ui/or-patterns/already-bound-name.stderr index 948c91370d0d4..9924b0d7f72eb 100644 --- a/src/test/ui/or-patterns/already-bound-name.stderr +++ b/src/test/ui/or-patterns/already-bound-name.stderr @@ -1,97 +1,89 @@ error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:12:13 + --> $DIR/already-bound-name.rs:11:13 | LL | let (a, a) = (0, 1); // Standard duplication without an or-pattern. | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:15:15 + --> $DIR/already-bound-name.rs:14:15 | LL | let (a, A(a, _) | B(a)) = (0, A(1, 2)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:15:25 + --> $DIR/already-bound-name.rs:14:25 | LL | let (a, A(a, _) | B(a)) = (0, A(1, 2)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:19:26 + --> $DIR/already-bound-name.rs:18:26 | LL | let (A(a, _) | B(a), a) = (A(0, 1), 2); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:22:14 + --> $DIR/already-bound-name.rs:21:14 | LL | let A(a, a) | B(a) = A(0, 1); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:25:21 + --> $DIR/already-bound-name.rs:24:21 | LL | let B(a) | A(a, a) = A(0, 1); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:29:21 + --> $DIR/already-bound-name.rs:28:21 | LL | B(a) | A(a, a) => {} // Let's ensure `match` has no funny business. | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:33:36 + --> $DIR/already-bound-name.rs:32:36 | LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:33:46 + --> $DIR/already-bound-name.rs:32:46 | LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:38:36 + --> $DIR/already-bound-name.rs:37:36 | LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:38:46 + --> $DIR/already-bound-name.rs:37:46 | LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); | ^ used in a pattern more than once error[E0408]: variable `a` is not bound in all patterns - --> $DIR/already-bound-name.rs:38:9 + --> $DIR/already-bound-name.rs:37:9 | LL | let B(_) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); | ^^^^ pattern doesn't bind `a` - variable not in all patterns error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:43:49 + --> $DIR/already-bound-name.rs:42:49 | LL | let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/already-bound-name.rs:43:59 + --> $DIR/already-bound-name.rs:42:59 | LL | let B(A(a, _) | B(a)) | A(A(a, _) | B(a), A(a, _) | B(a)) = B(B(1)); | ^ used in a pattern more than once -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/already-bound-name.rs:4:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error[E0308]: mismatched types - --> $DIR/already-bound-name.rs:33:31 + --> $DIR/already-bound-name.rs:32:31 | LL | let B(A(a, _) | B(a)) | A(a, A(a, _) | B(a)) = B(B(1)); | ^ ------- this expression has type `E>` diff --git a/src/test/ui/or-patterns/consistent-bindings.rs b/src/test/ui/or-patterns/consistent-bindings.rs index 0eb539dca4cba..ec71afed872ba 100644 --- a/src/test/ui/or-patterns/consistent-bindings.rs +++ b/src/test/ui/or-patterns/consistent-bindings.rs @@ -3,7 +3,6 @@ // edition:2018 #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete fn main() { // One level: diff --git a/src/test/ui/or-patterns/consistent-bindings.stderr b/src/test/ui/or-patterns/consistent-bindings.stderr index 433a02dfb3139..bb8e90af5f202 100644 --- a/src/test/ui/or-patterns/consistent-bindings.stderr +++ b/src/test/ui/or-patterns/consistent-bindings.stderr @@ -1,13 +1,5 @@ -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/consistent-bindings.rs:5:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error[E0308]: mismatched types - --> $DIR/consistent-bindings.rs:44:9 + --> $DIR/consistent-bindings.rs:43:9 | LL | let () = 0; | ^^ expected integer, found `()` diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs index 8b0be2e7a66b4..2e8baf978e251 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs @@ -1,14 +1,11 @@ #![feature(or_patterns)] - -#![allow(incomplete_features)] #![deny(unreachable_patterns)] // We wrap patterns in a tuple because top-level or-patterns are special-cased for now. fn main() { // Get the fatal error out of the way match (0u8,) { - (0 | _,) => {} - //~^ ERROR or-patterns are not fully implemented yet + (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet } match (0u8, 0u8) { @@ -17,7 +14,7 @@ fn main() { } match ((0u8,),) { //~^ ERROR non-exhaustive patterns: `((4u8..=std::u8::MAX))` - ((0 | 1,) | (2 | 3,),) => {}, + ((0 | 1,) | (2 | 3,),) => {} } match (Some(0u8),) { //~^ ERROR non-exhaustive patterns: `(Some(2u8..=std::u8::MAX))` diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index e6aa157d278c8..7fbd846a22f2b 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `(2u8..=std::u8::MAX, _)` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:14:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:13:11 | LL | match (0u8, 0u8) { | ^^^^^^^^^^ pattern `(2u8..=std::u8::MAX, _)` not covered @@ -7,7 +7,7 @@ LL | match (0u8, 0u8) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `((4u8..=std::u8::MAX))` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:18:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:17:11 | LL | match ((0u8,),) { | ^^^^^^^^^ pattern `((4u8..=std::u8::MAX))` not covered @@ -15,7 +15,7 @@ LL | match ((0u8,),) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `(Some(2u8..=std::u8::MAX))` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:22:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:21:11 | LL | match (Some(0u8),) { | ^^^^^^^^^^^^ pattern `(Some(2u8..=std::u8::MAX))` not covered @@ -23,7 +23,7 @@ LL | match (Some(0u8),) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error: or-patterns are not fully implemented yet - --> $DIR/exhaustiveness-non-exhaustive.rs:10:10 + --> $DIR/exhaustiveness-non-exhaustive.rs:9:10 | LL | (0 | _,) => {} | ^^^^^ diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index f0dc3447f3154..9b62810d29db9 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -1,14 +1,11 @@ #![feature(or_patterns)] - -#![allow(incomplete_features)] #![deny(unreachable_patterns)] // We wrap patterns in a tuple because top-level or-patterns are special-cased for now. fn main() { // Get the fatal error out of the way match (0,) { - (0 | _,) => {} - //~^ ERROR or-patterns are not fully implemented yet + (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet } match (0,) { @@ -27,11 +24,11 @@ fn main() { (Some(2..=255),) => {} } match ((0,),) { - ((0 | 1,) | (2 | 3,),) => {}, - ((_,),) => {}, + ((0 | 1,) | (2 | 3,),) => {} + ((_,),) => {} } match (&[0u8][..],) { - ([] | [0 | 1..=255] | [_, ..],) => {}, + ([] | [0 | 1..=255] | [_, ..],) => {} } match ((0, 0),) { diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.stderr b/src/test/ui/or-patterns/exhaustiveness-pass.stderr index 1f4278c4b8098..dc5a4186ac700 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-pass.stderr @@ -1,5 +1,5 @@ error: or-patterns are not fully implemented yet - --> $DIR/exhaustiveness-pass.rs:10:10 + --> $DIR/exhaustiveness-pass.rs:9:10 | LL | (0 | _,) => {} | ^^^^^ diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 81bc1176f572e..dd1c16f500028 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -1,14 +1,11 @@ #![feature(or_patterns)] - -#![allow(incomplete_features)] #![deny(unreachable_patterns)] // We wrap patterns in a tuple because top-level or-patterns are special-cased for now. fn main() { // Get the fatal error out of the way match (0u8,) { - (0 | _,) => {} - //~^ ERROR or-patterns are not fully implemented yet + (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet } match (0u8,) { @@ -29,9 +26,9 @@ fn main() { } match (0u8, 0u8) { (1 | 2, 3 | 4) => {} - (1, 3) => {} //~ ERROR unreachable pattern - (1, 4) => {} //~ ERROR unreachable pattern - (2, 4) => {} //~ ERROR unreachable pattern + (1, 3) => {} //~ ERROR unreachable pattern + (1, 4) => {} //~ ERROR unreachable pattern + (2, 4) => {} //~ ERROR unreachable pattern (2 | 1, 4) => {} //~ ERROR unreachable pattern (1, 5 | 6) => {} (1, 4 | 5) => {} //~ ERROR unreachable pattern @@ -40,18 +37,17 @@ fn main() { match (Some(0u8),) { (None | Some(1 | 2),) => {} (Some(1),) => {} //~ ERROR unreachable pattern - (None,) => {} //~ ERROR unreachable pattern + (None,) => {} //~ ERROR unreachable pattern _ => {} } match ((0u8,),) { - ((1 | 2,) | (3 | 4,),) => {}, - ((1..=4,),) => {}, //~ ERROR unreachable pattern - _ => {}, + ((1 | 2,) | (3 | 4,),) => {} + ((1..=4,),) => {} //~ ERROR unreachable pattern + _ => {} } match (0,) { - (1 - | 1,) => {} //~ ERROR unreachable + (1 | 1,) => {} //~ ERROR unreachable _ => {} } match [0; 2] { diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index 7f7bb929a0d27..1f07c27afad9c 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -1,107 +1,107 @@ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:16:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:15:9 | LL | (1,) => {} | ^^^^ | note: the lint level is defined here - --> $DIR/exhaustiveness-unreachable-pattern.rs:4:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:3:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:21:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:20:9 | LL | (2,) => {} | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:27:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:26:9 | LL | (1 | 2,) => {} | ^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:32:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:31:9 | LL | (1, 3) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:33:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:32:9 | LL | (1, 4) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:34:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:33:9 | LL | (2, 4) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:35:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:34:9 | LL | (2 | 1, 4) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:37:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:36:9 | LL | (1, 4 | 5) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:42:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:41:9 | LL | (Some(1),) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:43:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:42:9 | LL | (None,) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:48:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:47:9 | LL | ((1..=4,),) => {}, | ^^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:54:12 + --> $DIR/exhaustiveness-unreachable-pattern.rs:53:12 | LL | | 1,) => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:61:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:60:15 | LL | | 0] => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:59:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:58:15 | LL | | 0 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:69:10 + --> $DIR/exhaustiveness-unreachable-pattern.rs:68:10 | LL | [1 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:75:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:74:14 | LL | Some(0 | ^ error: or-patterns are not fully implemented yet - --> $DIR/exhaustiveness-unreachable-pattern.rs:10:10 + --> $DIR/exhaustiveness-unreachable-pattern.rs:9:10 | LL | (0 | _,) => {} | ^^^^^ diff --git a/src/test/ui/or-patterns/feature-gate-const-fn.rs b/src/test/ui/or-patterns/feature-gate-const-fn.rs index d73dcf2666648..d21cf3dc72c99 100644 --- a/src/test/ui/or-patterns/feature-gate-const-fn.rs +++ b/src/test/ui/or-patterns/feature-gate-const-fn.rs @@ -1,5 +1,4 @@ #![feature(or_patterns)] -#![allow(incomplete_features)] const fn foo((Ok(a) | Err(a)): Result) { //~^ ERROR or-pattern is not allowed in a `const fn` diff --git a/src/test/ui/or-patterns/feature-gate-const-fn.stderr b/src/test/ui/or-patterns/feature-gate-const-fn.stderr index 26143d2f19d33..112bc62517260 100644 --- a/src/test/ui/or-patterns/feature-gate-const-fn.stderr +++ b/src/test/ui/or-patterns/feature-gate-const-fn.stderr @@ -1,5 +1,5 @@ error[E0658]: or-pattern is not allowed in a `const fn` - --> $DIR/feature-gate-const-fn.rs:4:15 + --> $DIR/feature-gate-const-fn.rs:3:15 | LL | const fn foo((Ok(a) | Err(a)): Result) { | ^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | const fn foo((Ok(a) | Err(a)): Result) { = help: add `#![feature(const_if_match)]` to the crate attributes to enable error[E0658]: or-pattern is not allowed in a `const fn` - --> $DIR/feature-gate-const-fn.rs:7:9 + --> $DIR/feature-gate-const-fn.rs:6:9 | LL | let Ok(y) | Err(y) = x; | ^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let Ok(y) | Err(y) = x; = help: add `#![feature(const_if_match)]` to the crate attributes to enable error[E0658]: or-pattern is not allowed in a `const` - --> $DIR/feature-gate-const-fn.rs:13:9 + --> $DIR/feature-gate-const-fn.rs:12:9 | LL | let Ok(y) | Err(y) = x; | ^^^^^^^^^^^^^^ @@ -26,7 +26,7 @@ LL | let Ok(y) | Err(y) = x; = help: add `#![feature(const_if_match)]` to the crate attributes to enable error[E0658]: or-pattern is not allowed in a `static` - --> $DIR/feature-gate-const-fn.rs:19:9 + --> $DIR/feature-gate-const-fn.rs:18:9 | LL | let Ok(y) | Err(y) = x; | ^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL | let Ok(y) | Err(y) = x; = help: add `#![feature(const_if_match)]` to the crate attributes to enable error[E0658]: or-pattern is not allowed in a `static mut` - --> $DIR/feature-gate-const-fn.rs:25:9 + --> $DIR/feature-gate-const-fn.rs:24:9 | LL | let Ok(y) | Err(y) = x; | ^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let Ok(y) | Err(y) = x; = help: add `#![feature(const_if_match)]` to the crate attributes to enable error[E0658]: or-pattern is not allowed in a `const` - --> $DIR/feature-gate-const-fn.rs:32:13 + --> $DIR/feature-gate-const-fn.rs:31:13 | LL | let Ok(y) | Err(y) = x; | ^^^^^^^^^^^^^^ diff --git a/src/test/ui/or-patterns/inconsistent-modes.rs b/src/test/ui/or-patterns/inconsistent-modes.rs index 44836893ea2b2..28b5f0c02fef4 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.rs +++ b/src/test/ui/or-patterns/inconsistent-modes.rs @@ -1,8 +1,6 @@ // This test ensures that or patterns require binding mode consistency across arms. #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete - #![allow(non_camel_case_types)] fn main() { // One level: diff --git a/src/test/ui/or-patterns/inconsistent-modes.stderr b/src/test/ui/or-patterns/inconsistent-modes.stderr index 7c1638ff94d0f..c329f90596091 100644 --- a/src/test/ui/or-patterns/inconsistent-modes.stderr +++ b/src/test/ui/or-patterns/inconsistent-modes.stderr @@ -1,5 +1,5 @@ error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:9:25 + --> $DIR/inconsistent-modes.rs:7:25 | LL | let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); | - ^ bound in different ways @@ -7,7 +7,7 @@ LL | let Ok(a) | Err(ref a): Result<&u8, u8> = Ok(&0); | first binding error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:11:29 + --> $DIR/inconsistent-modes.rs:9:29 | LL | let Ok(ref mut a) | Err(a): Result = Ok(0); | - ^ bound in different ways @@ -15,25 +15,25 @@ LL | let Ok(ref mut a) | Err(a): Result = Ok(0); | first binding error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:13:33 + --> $DIR/inconsistent-modes.rs:11:33 | LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); | - first binding ^ bound in different ways error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:16:39 + --> $DIR/inconsistent-modes.rs:14:39 | LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); | - first binding ^ bound in different ways error[E0409]: variable `b` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:16:46 + --> $DIR/inconsistent-modes.rs:14:46 | LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); | - first binding ^ bound in different ways error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:22:38 + --> $DIR/inconsistent-modes.rs:20:38 | LL | let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); | - ^ bound in different ways @@ -41,23 +41,15 @@ LL | let Ok(Ok(a) | Err(a)) | Err(ref a) = Err(0); | first binding error[E0409]: variable `a` is bound in inconsistent ways within the same match arm - --> $DIR/inconsistent-modes.rs:26:34 + --> $DIR/inconsistent-modes.rs:24:34 | LL | let Ok([ Ok((Ok(ref a) | Err(a),)) | Err(a) ]) | Err(a) = Err(&1); | - ^ bound in different ways | | | first binding -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/inconsistent-modes.rs:3:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error[E0308]: mismatched types - --> $DIR/inconsistent-modes.rs:13:25 + --> $DIR/inconsistent-modes.rs:11:25 | LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); | ^^^^^^^^^ -------------------- expected due to this @@ -68,7 +60,7 @@ LL | let Ok(ref a) | Err(ref mut a): Result<&u8, &mut u8> = Ok(&0); found type `&mut &mut u8` error[E0308]: mismatched types - --> $DIR/inconsistent-modes.rs:16:31 + --> $DIR/inconsistent-modes.rs:14:31 | LL | let Ok((ref a, b)) | Err((ref mut a, ref b)) = Ok((0, &0)); | ^^^^^^^^^ ----------- this expression has type `std::result::Result<({integer}, &{integer}), (_, _)>` diff --git a/src/test/ui/or-patterns/missing-bindings.rs b/src/test/ui/or-patterns/missing-bindings.rs index b065028e7a5a4..67cf52fa8c418 100644 --- a/src/test/ui/or-patterns/missing-bindings.rs +++ b/src/test/ui/or-patterns/missing-bindings.rs @@ -3,8 +3,6 @@ // edition:2018 #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete - #![allow(non_camel_case_types)] fn main() {} diff --git a/src/test/ui/or-patterns/missing-bindings.stderr b/src/test/ui/or-patterns/missing-bindings.stderr index c73af7a42eec0..57270e4412351 100644 --- a/src/test/ui/or-patterns/missing-bindings.stderr +++ b/src/test/ui/or-patterns/missing-bindings.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/missing-bindings.rs:22:9 + --> $DIR/missing-bindings.rs:20:9 | LL | let alpha | beta | charlie = alpha; | ^^^^^ ---- ^^^^^^^ pattern doesn't bind `beta` @@ -8,7 +8,7 @@ LL | let alpha | beta | charlie = alpha; | pattern doesn't bind `beta` error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/missing-bindings.rs:24:14 + --> $DIR/missing-bindings.rs:22:14 | LL | Some(alpha | beta) => {} | ^^^^^ ---- variable not in all patterns @@ -16,7 +16,7 @@ LL | Some(alpha | beta) => {} | pattern doesn't bind `beta` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:36:19 + --> $DIR/missing-bindings.rs:34:19 | LL | let A(a, _) | _ = X; | - ^ pattern doesn't bind `a` @@ -24,7 +24,7 @@ LL | let A(a, _) | _ = X; | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:37:9 + --> $DIR/missing-bindings.rs:35:9 | LL | let _ | B(a) = X; | ^ - variable not in all patterns @@ -32,7 +32,7 @@ LL | let _ | B(a) = X; | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:38:9 + --> $DIR/missing-bindings.rs:36:9 | LL | let A(..) | B(a) = X; | ^^^^^ - variable not in all patterns @@ -40,7 +40,7 @@ LL | let A(..) | B(a) = X; | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:39:19 + --> $DIR/missing-bindings.rs:37:19 | LL | let A(a, _) | B(_) = X; | - ^^^^ pattern doesn't bind `a` @@ -48,7 +48,7 @@ LL | let A(a, _) | B(_) = X; | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:40:19 + --> $DIR/missing-bindings.rs:38:19 | LL | let A(_, a) | B(_) = X; | - ^^^^ pattern doesn't bind `a` @@ -56,7 +56,7 @@ LL | let A(_, a) | B(_) = X; | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:41:19 + --> $DIR/missing-bindings.rs:39:19 | LL | let A(a, b) | B(a) = X; | - ^^^^ pattern doesn't bind `b` @@ -64,7 +64,7 @@ LL | let A(a, b) | B(a) = X; | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:45:9 + --> $DIR/missing-bindings.rs:43:9 | LL | let A(A(..) | B(_), _) | B(a) = Y; | ^^^^^^^^^^^^^^^^^^ - variable not in all patterns @@ -72,7 +72,7 @@ LL | let A(A(..) | B(_), _) | B(a) = Y; | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:46:11 + --> $DIR/missing-bindings.rs:44:11 | LL | let A(A(..) | B(a), _) | B(A(a, _) | B(a)) = Y; | ^^^^^ - variable not in all patterns @@ -80,7 +80,7 @@ LL | let A(A(..) | B(a), _) | B(A(a, _) | B(a)) = Y; | pattern doesn't bind `a` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:21 + --> $DIR/missing-bindings.rs:46:21 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | - ^^^^ pattern doesn't bind `a` @@ -88,7 +88,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:21 + --> $DIR/missing-bindings.rs:46:21 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | - ^^^^ pattern doesn't bind `b` @@ -96,7 +96,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:11 + --> $DIR/missing-bindings.rs:46:11 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | ^^^^^^^ - variable not in all patterns @@ -104,7 +104,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | pattern doesn't bind `c` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:32 + --> $DIR/missing-bindings.rs:46:32 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | - ^^^^ pattern doesn't bind `a` @@ -112,7 +112,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | variable not in all patterns error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:32 + --> $DIR/missing-bindings.rs:46:32 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | - ^^^^ pattern doesn't bind `b` @@ -120,7 +120,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | variable not in all patterns error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:32 + --> $DIR/missing-bindings.rs:46:32 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | - ^^^^ pattern doesn't bind `c` @@ -128,7 +128,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | variable not in all patterns error[E0408]: variable `d` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:32 + --> $DIR/missing-bindings.rs:46:32 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | - ^^^^ pattern doesn't bind `d` @@ -136,7 +136,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | variable not in all patterns error[E0408]: variable `e` is not bound in all patterns - --> $DIR/missing-bindings.rs:48:9 + --> $DIR/missing-bindings.rs:46:9 | LL | let A(A(a, b) | B(c), d) | B(e) = Y; | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns @@ -144,7 +144,7 @@ LL | let A(A(a, b) | B(c), d) | B(e) = Y; | pattern doesn't bind `e` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:64:29 + --> $DIR/missing-bindings.rs:62:29 | LL | Ok(a) | Err(_), | - ^^^^^^ pattern doesn't bind `a` @@ -152,7 +152,7 @@ LL | Ok(a) | Err(_), | variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:72:21 + --> $DIR/missing-bindings.rs:70:21 | LL | A(_, a) | | - variable not in all patterns @@ -160,7 +160,7 @@ LL | B(b), | ^^^^ pattern doesn't bind `a` error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:71:21 + --> $DIR/missing-bindings.rs:69:21 | LL | A(_, a) | | ^^^^^^^ pattern doesn't bind `b` @@ -168,7 +168,7 @@ LL | B(b), | - variable not in all patterns error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:75:17 + --> $DIR/missing-bindings.rs:73:17 | LL | A(_, a) | | - variable not in all patterns @@ -177,7 +177,7 @@ LL | B(_) | ^^^^ pattern doesn't bind `a` error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:75:17 + --> $DIR/missing-bindings.rs:73:17 | LL | B(b), | - variable not in all patterns @@ -186,7 +186,7 @@ LL | B(_) | ^^^^ pattern doesn't bind `b` error[E0408]: variable `a` is not bound in all patterns - --> $DIR/missing-bindings.rs:79:13 + --> $DIR/missing-bindings.rs:77:13 | LL | B(Ok(a) | Err(a)) | - variable not in all patterns @@ -198,7 +198,7 @@ LL | V3(c), | ^^^^^ pattern doesn't bind `a` error[E0408]: variable `b` is not bound in all patterns - --> $DIR/missing-bindings.rs:60:13 + --> $DIR/missing-bindings.rs:58:13 | LL | / V1( LL | | @@ -216,7 +216,7 @@ LL | V3(c), | ^^^^^ pattern doesn't bind `b` error[E0408]: variable `c` is not bound in all patterns - --> $DIR/missing-bindings.rs:60:13 + --> $DIR/missing-bindings.rs:58:13 | LL | / V1( LL | | @@ -237,14 +237,6 @@ LL | | ) | LL | V3(c), | - variable not in all patterns -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/missing-bindings.rs:5:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0408`. diff --git a/src/test/ui/or-patterns/multiple-pattern-typo.rs b/src/test/ui/or-patterns/multiple-pattern-typo.rs index e308c0adb4eb8..702c9573741e7 100644 --- a/src/test/ui/or-patterns/multiple-pattern-typo.rs +++ b/src/test/ui/or-patterns/multiple-pattern-typo.rs @@ -1,5 +1,4 @@ #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash fn main() { let x = 3; diff --git a/src/test/ui/or-patterns/multiple-pattern-typo.stderr b/src/test/ui/or-patterns/multiple-pattern-typo.stderr index c71c760b1e303..cb32068ec0d5e 100644 --- a/src/test/ui/or-patterns/multiple-pattern-typo.stderr +++ b/src/test/ui/or-patterns/multiple-pattern-typo.stderr @@ -1,5 +1,5 @@ error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:8:15 + --> $DIR/multiple-pattern-typo.rs:7:15 | LL | 1 | 2 || 3 => (), | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -7,7 +7,7 @@ LL | 1 | 2 || 3 => (), | while parsing this or-pattern starting here error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:13:16 + --> $DIR/multiple-pattern-typo.rs:12:16 | LL | (1 | 2 || 3) => (), | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -15,7 +15,7 @@ LL | (1 | 2 || 3) => (), | while parsing this or-pattern starting here error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:18:16 + --> $DIR/multiple-pattern-typo.rs:17:16 | LL | (1 | 2 || 3,) => (), | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -23,7 +23,7 @@ LL | (1 | 2 || 3,) => (), | while parsing this or-pattern starting here error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:25:18 + --> $DIR/multiple-pattern-typo.rs:24:18 | LL | TS(1 | 2 || 3) => (), | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -31,7 +31,7 @@ LL | TS(1 | 2 || 3) => (), | while parsing this or-pattern starting here error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:32:23 + --> $DIR/multiple-pattern-typo.rs:31:23 | LL | NS { f: 1 | 2 || 3 } => (), | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -39,7 +39,7 @@ LL | NS { f: 1 | 2 || 3 } => (), | while parsing this or-pattern starting here error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:37:16 + --> $DIR/multiple-pattern-typo.rs:36:16 | LL | [1 | 2 || 3] => (), | - ^^ help: use a single `|` to separate multiple alternative patterns: `|` @@ -47,18 +47,10 @@ LL | [1 | 2 || 3] => (), | while parsing this or-pattern starting here error: unexpected token `||` after pattern - --> $DIR/multiple-pattern-typo.rs:42:9 + --> $DIR/multiple-pattern-typo.rs:41:9 | LL | || 1 | 2 | 3 => (), | ^^ help: use a single `|` to separate multiple alternative patterns: `|` -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/multiple-pattern-typo.rs:1:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error: aborting due to 7 previous errors diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs index ce6836f30f946..d23220056524b 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.rs @@ -2,7 +2,6 @@ // This is not a semantic test. We only test parsing. #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash fn main() {} diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr index e77d92e8b07d9..6cbb59dc22031 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr +++ b/src/test/ui/or-patterns/or-patterns-syntactic-fail.stderr @@ -1,53 +1,53 @@ error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:28:13 + --> $DIR/or-patterns-syntactic-fail.rs:27:13 | LL | fn fun1(A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is not allowed in a parameter pattern - --> $DIR/or-patterns-syntactic-fail.rs:30:13 + --> $DIR/or-patterns-syntactic-fail.rs:29:13 | LL | fn fun2(| A | B: E) {} | ^ help: remove the `|` error: an or-pattern parameter must be wrapped in parenthesis - --> $DIR/or-patterns-syntactic-fail.rs:30:15 + --> $DIR/or-patterns-syntactic-fail.rs:29:15 | LL | fn fun2(| A | B: E) {} | ^^^^^ help: wrap the pattern in parenthesis: `(A | B)` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:41:11 + --> $DIR/or-patterns-syntactic-fail.rs:40:11 | LL | let ( | A | B) = E::A; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:42:11 + --> $DIR/or-patterns-syntactic-fail.rs:41:11 | LL | let ( | A | B,) = (E::B,); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:43:11 + --> $DIR/or-patterns-syntactic-fail.rs:42:11 | LL | let [ | A | B ] = [E::A]; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:44:13 + --> $DIR/or-patterns-syntactic-fail.rs:43:13 | LL | let TS( | A | B ); | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:45:17 + --> $DIR/or-patterns-syntactic-fail.rs:44:17 | LL | let NS { f: | A | B }; | ^ help: remove the `|` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:47:11 + --> $DIR/or-patterns-syntactic-fail.rs:46:11 | LL | let ( || A | B) = E::A; | ^^ help: remove the `||` @@ -55,7 +55,7 @@ LL | let ( || A | B) = E::A; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:48:11 + --> $DIR/or-patterns-syntactic-fail.rs:47:11 | LL | let [ || A | B ] = [E::A]; | ^^ help: remove the `||` @@ -63,7 +63,7 @@ LL | let [ || A | B ] = [E::A]; = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:49:13 + --> $DIR/or-patterns-syntactic-fail.rs:48:13 | LL | let TS( || A | B ); | ^^ help: remove the `||` @@ -71,7 +71,7 @@ LL | let TS( || A | B ); = note: alternatives in or-patterns are separated with `|`, not `||` error: a leading `|` is only allowed in a top-level pattern - --> $DIR/or-patterns-syntactic-fail.rs:50:17 + --> $DIR/or-patterns-syntactic-fail.rs:49:17 | LL | let NS { f: || A | B }; | ^^ help: remove the `||` @@ -79,7 +79,7 @@ LL | let NS { f: || A | B }; = note: alternatives in or-patterns are separated with `|`, not `||` error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:14:15 + --> $DIR/or-patterns-syntactic-fail.rs:13:15 | LL | macro_rules! accept_pat { | ----------------------- when calling this macro @@ -88,7 +88,7 @@ LL | accept_pat!(p | q); | ^ no rules expected this token in macro call error: no rules expected the token `|` - --> $DIR/or-patterns-syntactic-fail.rs:15:13 + --> $DIR/or-patterns-syntactic-fail.rs:14:13 | LL | macro_rules! accept_pat { | ----------------------- when calling this macro @@ -96,16 +96,8 @@ LL | macro_rules! accept_pat { LL | accept_pat!(| p | q); | ^ no rules expected this token in macro call -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/or-patterns-syntactic-fail.rs:4:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error[E0369]: no implementation for `E | ()` - --> $DIR/or-patterns-syntactic-fail.rs:24:22 + --> $DIR/or-patterns-syntactic-fail.rs:23:22 | LL | let _ = |A | B: E| (); | ----^ -- () @@ -115,7 +107,7 @@ LL | let _ = |A | B: E| (); = note: an implementation of `std::ops::BitOr` might be missing for `E` error[E0308]: mismatched types - --> $DIR/or-patterns-syntactic-fail.rs:52:36 + --> $DIR/or-patterns-syntactic-fail.rs:51:36 | LL | let recovery_witness: String = 0; | ------ ^ diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs index 73c1477c281a8..5fe72caf9c1ff 100644 --- a/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs +++ b/src/test/ui/or-patterns/or-patterns-syntactic-pass.rs @@ -3,7 +3,7 @@ // check-pass -#![feature(or_patterns)] //~ WARNING the feature `or_patterns` is incomplete +#![feature(or_patterns)] fn main() {} diff --git a/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr b/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr deleted file mode 100644 index 3145a2e9f2a6e..0000000000000 --- a/src/test/ui/or-patterns/or-patterns-syntactic-pass.stderr +++ /dev/null @@ -1,8 +0,0 @@ -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/or-patterns-syntactic-pass.rs:6:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - diff --git a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs index 89ea2d5181945..e8b5b492b7738 100644 --- a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs +++ b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.rs @@ -3,7 +3,6 @@ #![feature(bindings_after_at)] #![feature(or_patterns)] -//~^ WARN the feature `or_patterns` is incomplete and may cause the compiler to crash fn main() { fn f(a @ a @ a: ()) {} diff --git a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr index c568d2a3aa2b8..cba17d82e93c5 100644 --- a/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr +++ b/src/test/ui/pattern/bindings-after-at/pat-at-same-name-both.stderr @@ -1,71 +1,63 @@ error[E0415]: identifier `a` is bound more than once in this parameter list - --> $DIR/pat-at-same-name-both.rs:9:14 + --> $DIR/pat-at-same-name-both.rs:8:14 | LL | fn f(a @ a @ a: ()) {} | ^ used as parameter more than once error[E0415]: identifier `a` is bound more than once in this parameter list - --> $DIR/pat-at-same-name-both.rs:9:18 + --> $DIR/pat-at-same-name-both.rs:8:18 | LL | fn f(a @ a @ a: ()) {} | ^ used as parameter more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:14:20 + --> $DIR/pat-at-same-name-both.rs:13:20 | LL | Ok(a @ b @ a) | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:16:23 + --> $DIR/pat-at-same-name-both.rs:15:23 | LL | | Err(a @ b @ a) | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:21:13 + --> $DIR/pat-at-same-name-both.rs:20:13 | LL | let a @ a @ a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:21:17 + --> $DIR/pat-at-same-name-both.rs:20:17 | LL | let a @ a @ a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:24:21 + --> $DIR/pat-at-same-name-both.rs:23:21 | LL | let ref a @ ref a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:26:29 + --> $DIR/pat-at-same-name-both.rs:25:29 | LL | let ref mut a @ ref mut a = (); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:29:17 + --> $DIR/pat-at-same-name-both.rs:28:17 | LL | let a @ (Ok(a) | Err(a)) = Ok(()); | ^ used in a pattern more than once error[E0416]: identifier `a` is bound more than once in the same pattern - --> $DIR/pat-at-same-name-both.rs:29:26 + --> $DIR/pat-at-same-name-both.rs:28:26 | LL | let a @ (Ok(a) | Err(a)) = Ok(()); | ^ used in a pattern more than once -warning: the feature `or_patterns` is incomplete and may cause the compiler to crash - --> $DIR/pat-at-same-name-both.rs:5:12 - | -LL | #![feature(or_patterns)] - | ^^^^^^^^^^^ - | - = note: `#[warn(incomplete_features)]` on by default - error: aborting due to 10 previous errors Some errors have detailed explanations: E0415, E0416. From eecee76652dafe25f2cd6453f33bf4c298e84463 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 12:25:53 +0000 Subject: [PATCH 02/13] Generate prebinding blocks lazily --- src/librustc_mir_build/build/matches/mod.rs | 217 +++++++++----------- 1 file changed, 100 insertions(+), 117 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index ad55a9fb7b81a..1eb0c2cb3e65e 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -26,7 +26,6 @@ mod simplify; mod test; mod util; -use itertools::Itertools; use std::convert::TryFrom; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -66,12 +65,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// We generate MIR in the following steps: /// /// 1. Evaluate the scrutinee and add the fake read of it ([Builder::lower_scrutinee]). - /// 2. Create the prebinding and otherwise blocks ([Builder::create_match_candidates]). - /// 3. Create the decision tree ([Builder::lower_match_tree]). - /// 4. Determine the fake borrows that are needed from the places that were + /// 2. Create the decision tree ([Builder::lower_match_tree]). + /// 3. Determine the fake borrows that are needed from the places that were /// matched against and create the required temporaries for them /// ([Builder::calculate_fake_borrows]). - /// 5. Create everything else: the guards and the arms ([Builder::lower_match_arms]). + /// 4. Create everything else: the guards and the arms ([Builder::lower_match_arms]). /// /// ## False edges /// @@ -148,13 +146,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee: &Place<'tcx>, arms: &'pat [Arm<'tcx>], ) -> Vec<(&'pat Arm<'tcx>, Vec>)> { - let candidate_count = arms.iter().map(|c| c.top_pats_hack().len()).sum::(); - let pre_binding_blocks: Vec<_> = - (0..candidate_count).map(|_| self.cfg.start_new_block()).collect(); - - let mut candidate_pre_binding_blocks = pre_binding_blocks.iter(); - let mut next_candidate_pre_binding_blocks = pre_binding_blocks.iter().skip(1); - // Assemble a list of candidates: there is one candidate per pattern, // which means there may be more than one candidate *per arm*. arms.iter() @@ -163,21 +154,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_candidates: Vec<_> = arm .top_pats_hack() .iter() - .zip(candidate_pre_binding_blocks.by_ref()) - .map(|(pattern, pre_binding_block)| Candidate { + .map(|pattern| Candidate { span: pattern.span, + has_guard: arm_has_guard, match_pairs: smallvec![MatchPair::new(*scrutinee, pattern)], bindings: vec![], ascriptions: vec![], - otherwise_block: if arm_has_guard { - Some(self.cfg.start_new_block()) - } else { - None - }, - pre_binding_block: *pre_binding_block, - next_candidate_pre_binding_block: next_candidate_pre_binding_blocks - .next() - .copied(), + otherwise_block: None, + pre_binding_block: None, + next_candidate_pre_binding_block: None, }) .collect(); (arm, arm_candidates) @@ -203,16 +188,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // them. let mut fake_borrows = if match_has_guard { Some(FxHashSet::default()) } else { None }; + let mut otherwise = None; + // This will generate code to test scrutinee_place and // branch to the appropriate arm block self.match_candidates( scrutinee_span, - &mut Some(block), - None, + block, + &mut otherwise, &mut candidates, &mut fake_borrows, ); + if let Some(otherwise_block) = otherwise { + let source_info = self.source_info(scrutinee_span); + self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); + } + + let mut next_prebinding_block = None; + + for candidate in candidates.iter_mut().rev() { + candidate.next_candidate_pre_binding_block = next_prebinding_block; + next_prebinding_block = candidate.pre_binding_block; + } + if let Some(ref borrows) = fake_borrows { self.calculate_fake_borrows(borrows, scrutinee_span) } else { @@ -427,13 +426,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // create a dummy candidate let mut candidate = Candidate { span: irrefutable_pat.span, + has_guard: false, match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)], bindings: vec![], ascriptions: vec![], // since we don't call `match_candidates`, next fields are unused otherwise_block: None, - pre_binding_block: block, + pre_binding_block: None, next_candidate_pre_binding_block: None, }; @@ -645,6 +645,8 @@ crate struct Candidate<'pat, 'tcx> { // span of the original pattern that gave rise to this candidate span: Span, + has_guard: bool, + // all of these must be satisfied... match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, @@ -658,7 +660,7 @@ crate struct Candidate<'pat, 'tcx> { otherwise_block: Option, // ...and the blocks for add false edges between candidates - pre_binding_block: BasicBlock, + pre_binding_block: Option, next_candidate_pre_binding_block: Option, } @@ -758,13 +760,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// which of these candidates, if any, is the correct one. The /// candidates are sorted such that the first item in the list /// has the highest priority. When a candidate is found to match - /// the value, we will generate a branch to the appropriate + /// the value, we will set and generate a branch to the appropriate /// prebinding block. /// /// If we find that *NONE* of the candidates apply, we branch to the - /// `otherwise_block`. In principle, this means that the input list was not - /// exhaustive, though at present we sometimes are not smart enough to - /// recognize all exhaustive inputs. + /// `otherwise_block`, setting it to `Some` if required. In principle, this + /// means that the input list was not exhaustive, though at present we + /// sometimes are not smart enough to recognize all exhaustive inputs. /// /// It might be surprising that the input can be inexhaustive. /// Indeed, initially, it is not, because all matches are @@ -778,8 +780,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn match_candidates<'pat>( &mut self, span: Span, - start_block: &mut Option, - otherwise_block: Option, + start_block: BasicBlock, + otherwise_block: &mut Option, candidates: &mut [&mut Candidate<'pat, 'tcx>], fake_borrows: &mut Option>>, ) { @@ -802,7 +804,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("match_candidates: {:?} candidates fully matched", fully_matched); let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); - let block: BasicBlock = if !matched_candidates.is_empty() { + let block = if !matched_candidates.is_empty() { let otherwise_block = self.select_matched_candidates(matched_candidates, start_block, fake_borrows); @@ -816,7 +818,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.start_new_block() } } else { - *start_block.get_or_insert_with(|| self.cfg.start_new_block()) + start_block }; // If there are no candidates that still need testing, we're @@ -824,9 +826,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // never reach this point. if unmatched_candidates.is_empty() { let source_info = self.source_info(span); - match otherwise_block { - Some(otherwise) => self.cfg.goto(block, source_info, otherwise), - None => self.cfg.terminate(block, source_info, TerminatorKind::Unreachable), + if let Some(otherwise) = *otherwise_block { + self.cfg.goto(block, source_info, otherwise); + } else { + *otherwise_block = Some(block); } return; } @@ -856,7 +859,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn select_matched_candidates( &mut self, matched_candidates: &mut [&mut Candidate<'_, 'tcx>], - start_block: &mut Option, + start_block: BasicBlock, fake_borrows: &mut Option>>, ) -> Option { debug_assert!( @@ -899,66 +902,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fully_matched_with_guard = matched_candidates .iter() - .position(|c| c.otherwise_block.is_none()) + .position(|c| !c.has_guard) .unwrap_or(matched_candidates.len() - 1); let (reachable_candidates, unreachable_candidates) = matched_candidates.split_at_mut(fully_matched_with_guard + 1); - let first_candidate = &reachable_candidates[0]; - let first_prebinding_block = first_candidate.pre_binding_block; + let mut next_prebinding = start_block; - // `goto -> first_prebinding_block` from the `start_block` if there is one. - if let Some(start_block) = *start_block { - let source_info = self.source_info(first_candidate.span); - self.cfg.goto(start_block, source_info, first_prebinding_block); - } else { - *start_block = Some(first_prebinding_block); - } - - for (first_candidate, second_candidate) in reachable_candidates.iter().tuple_windows() { - let source_info = self.source_info(first_candidate.span); - if let Some(otherwise_block) = first_candidate.otherwise_block { - self.false_edges( - otherwise_block, - second_candidate.pre_binding_block, - first_candidate.next_candidate_pre_binding_block, - source_info, - ); - } else { - bug!("candidate other than the last has no guard"); + for candidate in reachable_candidates.iter_mut() { + assert!(candidate.otherwise_block.is_none()); + assert!(candidate.pre_binding_block.is_none()); + candidate.pre_binding_block = Some(next_prebinding); + if candidate.has_guard { + next_prebinding = self.cfg.start_new_block(); + candidate.otherwise_block = Some(next_prebinding); } } - debug!("match_candidates: add false edges for unreachable {:?}", unreachable_candidates); + debug!( + "match_candidates: add pre_binding_blocks for unreachable {:?}", + unreachable_candidates, + ); for candidate in unreachable_candidates { - if let Some(otherwise) = candidate.otherwise_block { - let source_info = self.source_info(candidate.span); - let unreachable = self.cfg.start_new_block(); - self.false_edges( - otherwise, - unreachable, - candidate.next_candidate_pre_binding_block, - source_info, - ); - self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); - } + assert!(candidate.pre_binding_block.is_none()); + candidate.pre_binding_block = Some(self.cfg.start_new_block()); } - let last_candidate = reachable_candidates.last().unwrap(); - if let Some(otherwise) = last_candidate.otherwise_block { - let source_info = self.source_info(last_candidate.span); - let block = self.cfg.start_new_block(); - self.false_edges( - otherwise, - block, - last_candidate.next_candidate_pre_binding_block, - source_info, - ); - Some(block) - } else { - None - } + reachable_candidates.last_mut().unwrap().otherwise_block } /// This is the most subtle part of the matching algorithm. At @@ -1078,7 +1049,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>], block: BasicBlock, - mut otherwise_block: Option, + otherwise_block: &mut Option, fake_borrows: &mut Option>>, ) { // extract the match-pair from the highest priority candidate @@ -1150,49 +1121,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // improves the speed of llvm when optimizing long string literal // matches let make_target_blocks = move |this: &mut Self| -> Vec { + // The block that we should branch to if none of the + // `target_candidates` match. This is either the block where we + // start matching the untested candidates if there are any, + // otherwise it's the `otherwise_block`. + let remainder_start = &mut None; + let remainder_start = + if candidates.is_empty() { &mut *otherwise_block } else { remainder_start }; + // For each outcome of test, process the candidates that still // apply. Collect a list of blocks where control flow will // branch if one of the `target_candidate` sets is not // exhaustive. - if !candidates.is_empty() { - let remainder_start = &mut None; - this.match_candidates( - span, - remainder_start, - otherwise_block, - candidates, - fake_borrows, - ); - otherwise_block = Some(remainder_start.unwrap()); - }; - - target_candidates + let target_blocks: Vec<_> = target_candidates .into_iter() .map(|mut candidates| { if candidates.len() != 0 { - let candidate_start = &mut None; + let candidate_start = this.cfg.start_new_block(); this.match_candidates( span, candidate_start, - otherwise_block, + remainder_start, &mut *candidates, fake_borrows, ); - candidate_start.unwrap() + candidate_start } else { - *otherwise_block.get_or_insert_with(|| { - let unreachable = this.cfg.start_new_block(); - let source_info = this.source_info(span); - this.cfg.terminate( - unreachable, - source_info, - TerminatorKind::Unreachable, - ); - unreachable - }) + *remainder_start.get_or_insert_with(|| this.cfg.start_new_block()) } }) - .collect() + .collect(); + + if !candidates.is_empty() { + let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block()); + this.match_candidates( + span, + remainder_start, + otherwise_block, + candidates, + fake_borrows, + ); + }; + + target_blocks }; self.perform_test(block, &match_place, &test, make_target_blocks); @@ -1297,7 +1268,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let candidate_source_info = self.source_info(candidate.span); - let mut block = candidate.pre_binding_block; + let mut block = candidate.pre_binding_block.unwrap(); // If we are adding our own statements, then we need a fresh block. let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some() @@ -1437,11 +1408,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(temp)); } + let otherwise_block = candidate.otherwise_block.unwrap_or_else(|| { + let unreachable = self.cfg.start_new_block(); + self.cfg.terminate(unreachable, source_info, TerminatorKind::Unreachable); + unreachable + }); + let outside_scope = self.cfg.start_new_block(); self.exit_scope( source_info.span, region_scope, otherwise_post_guard_block, - candidate.otherwise_block.unwrap(), + outside_scope, + ); + self.false_edges( + outside_scope, + otherwise_block, + candidate.next_candidate_pre_binding_block, + source_info, ); // We want to ensure that the matched candidates are bound From 64eab7750bed3d692c90b215c5a3c65030ece3b7 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 12:42:52 +0000 Subject: [PATCH 03/13] Don't create unnecessary block --- src/librustc_mir_build/build/matches/mod.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index 1eb0c2cb3e65e..a060c8fd2a215 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -1270,13 +1270,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut block = candidate.pre_binding_block.unwrap(); - // If we are adding our own statements, then we need a fresh block. - let create_fresh_block = candidate.next_candidate_pre_binding_block.is_some() - || !candidate.bindings.is_empty() - || !candidate.ascriptions.is_empty() - || guard.is_some(); - - if create_fresh_block { + if candidate.next_candidate_pre_binding_block.is_some() { let fresh_block = self.cfg.start_new_block(); self.false_edges( block, @@ -1285,11 +1279,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate_source_info, ); block = fresh_block; - self.ascribe_types(block, &candidate.ascriptions); - } else { - return block; } + self.ascribe_types(block, &candidate.ascriptions); + // rust-lang/rust#27282: The `autoref` business deserves some // explanation here. // From 5cc4352bc4f9789d7243e79684b2943d3d9ad450 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 13:39:43 +0000 Subject: [PATCH 04/13] Implement general or-patterns in `match` expressions --- src/librustc_mir_build/build/matches/mod.rs | 394 ++++++++++++++---- .../build/matches/simplify.rs | 58 ++- src/librustc_mir_build/build/matches/test.rs | 6 +- src/librustc_mir_build/hair/mod.rs | 11 - 4 files changed, 381 insertions(+), 88 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index a060c8fd2a215..4ac3ced17dbb1 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -26,7 +26,9 @@ mod simplify; mod test; mod util; +use std::borrow::Borrow; use std::convert::TryFrom; +use std::mem; impl<'a, 'tcx> Builder<'a, 'tcx> { /// Generates MIR for a `match` expression. @@ -95,7 +97,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); let candidates = - arm_candidates.iter_mut().flat_map(|(_, candidates)| candidates).collect::>(); + arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::>(); let fake_borrow_temps = self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates); @@ -145,27 +147,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, scrutinee: &Place<'tcx>, arms: &'pat [Arm<'tcx>], - ) -> Vec<(&'pat Arm<'tcx>, Vec>)> { + ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> { // Assemble a list of candidates: there is one candidate per pattern, // which means there may be more than one candidate *per arm*. arms.iter() .map(|arm| { let arm_has_guard = arm.guard.is_some(); - let arm_candidates: Vec<_> = arm - .top_pats_hack() - .iter() - .map(|pattern| Candidate { - span: pattern.span, - has_guard: arm_has_guard, - match_pairs: smallvec![MatchPair::new(*scrutinee, pattern)], - bindings: vec![], - ascriptions: vec![], - otherwise_block: None, - pre_binding_block: None, - next_candidate_pre_binding_block: None, - }) - .collect(); - (arm, arm_candidates) + let arm_candidate = Candidate { + span: arm.pattern.span, + match_pairs: smallvec![MatchPair::new(*scrutinee, &arm.pattern),], + bindings: vec![], + ascriptions: vec![], + has_guard: arm_has_guard, + needs_otherwise_block: arm_has_guard, + otherwise_block: None, + pre_binding_block: None, + next_candidate_pre_binding_block: None, + subcandidates: vec![], + }; + (arm, arm_candidate) }) .collect() } @@ -205,11 +205,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); } - let mut next_prebinding_block = None; + let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; - for candidate in candidates.iter_mut().rev() { - candidate.next_candidate_pre_binding_block = next_prebinding_block; - next_prebinding_block = candidate.pre_binding_block; + for candidate in candidates.into_iter() { + candidate.visit_leaves(|leaf_candidate| { + if let Some(ref mut prev) = previous_candidate { + prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; + } + previous_candidate = Some(leaf_candidate); + }); } if let Some(ref borrows) = fake_borrows { @@ -230,7 +234,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { destination: &Place<'tcx>, scrutinee_place: Place<'tcx>, scrutinee_span: Span, - arm_candidates: Vec<(&'_ Arm<'tcx>, Vec>)>, + arm_candidates: Vec<(&'_ Arm<'tcx>, Candidate<'_, 'tcx>)>, outer_source_info: SourceInfo, fake_borrow_temps: Vec<(Place<'tcx>, Local)>, ) -> BlockAnd<()> { @@ -238,8 +242,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let arm_end_blocks: Vec<_> = arm_candidates .into_iter() - .map(|(arm, candidates)| { - debug!("lowering arm {:?}\ncanidates = {:?}", arm, candidates); + .map(|(arm, candidate)| { + debug!("lowering arm {:?}\ncanidate = {:?}", arm, candidate); let arm_source_info = self.source_info(arm.span); let arm_scope = (arm.scope, arm_source_info); @@ -248,14 +252,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scope = this.declare_bindings( None, arm.span, - &arm.top_pats_hack()[0], + &arm.pattern, ArmHasGuard(arm.guard.is_some()), Some((Some(&scrutinee_place), scrutinee_span)), ); let arm_block = this.bind_pattern( outer_source_info, - candidates, + candidate, arm.guard.as_ref().map(|g| (g, match_scope)), &fake_borrow_temps, scrutinee_span, @@ -289,35 +293,52 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_pattern( &mut self, outer_source_info: SourceInfo, - mut candidates: Vec>, + candidate: Candidate<'_, 'tcx>, guard: Option<(&Guard<'tcx>, region::Scope)>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, arm_scope: region::Scope, ) -> BasicBlock { - if candidates.len() == 1 { + if candidate.subcandidates.is_empty() { // Avoid generating another `BasicBlock` when we only have one // candidate. self.bind_and_guard_matched_candidate( - candidates.pop().unwrap(), + candidate, + &[], guard, fake_borrow_temps, scrutinee_span, ) } else { - let arm_block = self.cfg.start_new_block(); - for candidate in candidates { - // Avoid scheduling drops multiple times. - self.clear_top_scope(arm_scope); - let binding_end = self.bind_and_guard_matched_candidate( - candidate, - guard, - fake_borrow_temps, - scrutinee_span, - ); - self.cfg.goto(binding_end, outer_source_info, arm_block); - } - arm_block + let target_block = self.cfg.start_new_block(); + + // We keep a stack of all of the bindings and type asciptions + // from the the parent candidates that we visit, that also need to + // be bound for each candidate. + traverse_candidate( + candidate, + &mut Vec::new(), + &mut |leaf_candidate, parent_bindings| { + self.clear_top_scope(arm_scope); + let binding_end = self.bind_and_guard_matched_candidate( + leaf_candidate, + parent_bindings, + guard, + &fake_borrow_temps, + scrutinee_span, + ); + self.cfg.goto(binding_end, outer_source_info, target_block); + }, + |inner_candidate, parent_bindings| { + parent_bindings.push((inner_candidate.bindings, inner_candidate.ascriptions)); + inner_candidate.subcandidates.into_iter() + }, + |parent_bindings| { + parent_bindings.pop(); + }, + ); + + target_block } } @@ -427,6 +448,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut candidate = Candidate { span: irrefutable_pat.span, has_guard: false, + needs_otherwise_block: false, match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)], bindings: vec![], ascriptions: vec![], @@ -435,6 +457,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise_block: None, pre_binding_block: None, next_candidate_pre_binding_block: None, + subcandidates: vec![], }; // Simplify the candidate. Since the pattern is irrefutable, this should @@ -632,9 +655,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } PatKind::Or { ref pats } => { - for pat in pats { - self.visit_bindings(&pat, pattern_user_ty.clone(), f); - } + self.visit_bindings(&pats[0], pattern_user_ty.clone(), f); } } } @@ -642,28 +663,72 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { #[derive(Debug)] crate struct Candidate<'pat, 'tcx> { - // span of the original pattern that gave rise to this candidate + /// `Span` of the original pattern that gave rise to this candidate span: Span, + /// This `Candidate` has a guard. has_guard: bool, - // all of these must be satisfied... + /// This `Candidate` needs and otherwise block, either because it has a + /// guard or it has subcandidates. + needs_otherwise_block: bool, + + /// All of these must be satisfied... match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, - // ...these bindings established... + /// ...these bindings established... bindings: Vec>, - // ...and these types asserted... + /// ...and these types asserted... ascriptions: Vec>, - // ...and the guard must be evaluated, if false branch to Block... + /// ... and if this is non-empty, one of these subcandidates also has to match ... + subcandidates: Vec>, + + /// ...and the guard must be evaluated, if false branch to Block... otherwise_block: Option, - // ...and the blocks for add false edges between candidates + /// ...and the blocks for add false edges between candidates pre_binding_block: Option, next_candidate_pre_binding_block: Option, } +impl Candidate<'_, '_> { + /// Visit the leaf candidates (those with no subcandidates) contained in + /// this candidate. + fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) { + traverse_candidate( + self, + &mut (), + &mut move |c, _| visit_leaf(c), + move |c, _| c.subcandidates.iter_mut(), + |_| {}, + ); + } +} + +/// A depth-first traversal of the `Candidate` and all of its recursive +/// subcandidates. +fn traverse_candidate<'pat, 'tcx: 'pat, C, T, I>( + candidate: C, + context: &mut T, + visit_leaf: &mut impl FnMut(C, &mut T), + get_children: impl Copy + Fn(C, &mut T) -> I, + complete_children: impl Copy + Fn(&mut T), +) where + C: Borrow>, + I: Iterator, +{ + if candidate.borrow().subcandidates.is_empty() { + visit_leaf(candidate, context) + } else { + for child in get_children(candidate, context) { + traverse_candidate(child, context, visit_leaf, get_children, complete_children); + } + complete_children(context) + } +} + #[derive(Clone, Debug)] struct Binding<'tcx> { span: Span, @@ -793,10 +858,45 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Start by simplifying candidates. Once this process is complete, all // the match pairs which remain require some form of test, whether it // be a switch or pattern comparison. + let mut split_or_candidate = false; for candidate in &mut *candidates { - self.simplify_candidate(candidate); + split_or_candidate |= self.simplify_candidate(candidate); } + if split_or_candidate { + // At least one of the candidates has been split into subcandidates. + // We need to change the candidate list to include those. + let mut new_candidates = Vec::new(); + + for candidate in candidates { + candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate)); + } + self.match_simplified_candidates( + span, + start_block, + otherwise_block, + &mut *new_candidates, + fake_borrows, + ); + } else { + self.match_simplified_candidates( + span, + start_block, + otherwise_block, + candidates, + fake_borrows, + ); + }; + } + + fn match_simplified_candidates( + &mut self, + span: Span, + start_block: BasicBlock, + otherwise_block: &mut Option, + candidates: &mut [&mut Candidate<_, 'tcx>], + fake_borrows: &mut Option>>, + ) { // The candidates are sorted by priority. Check to see whether the // higher priority candidates (and hence at the front of the slice) // have satisfied all their match pairs. @@ -835,7 +935,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // Test for the remaining candidates. - self.test_candidates(span, unmatched_candidates, block, otherwise_block, fake_borrows); + self.test_candidates_with_or( + span, + unmatched_candidates, + block, + otherwise_block, + fake_borrows, + ); } /// Link up matched candidates. For example, if we have something like @@ -866,6 +972,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { !matched_candidates.is_empty(), "select_matched_candidates called with no candidates", ); + debug_assert!( + matched_candidates.iter().all(|c| c.subcandidates.is_empty()), + "subcandidates should be empty in select_matched_candidates", + ); // Insert a borrows of prefixes of places that are bound and are // behind a dereference projection. @@ -902,7 +1012,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fully_matched_with_guard = matched_candidates .iter() - .position(|c| !c.has_guard) + .position(|c| !c.needs_otherwise_block) .unwrap_or(matched_candidates.len() - 1); let (reachable_candidates, unreachable_candidates) = @@ -914,7 +1024,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert!(candidate.otherwise_block.is_none()); assert!(candidate.pre_binding_block.is_none()); candidate.pre_binding_block = Some(next_prebinding); - if candidate.has_guard { + if candidate.needs_otherwise_block { next_prebinding = self.cfg.start_new_block(); candidate.otherwise_block = Some(next_prebinding); } @@ -932,6 +1042,120 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { reachable_candidates.last_mut().unwrap().otherwise_block } + fn test_candidates_with_or( + &mut self, + span: Span, + candidates: &mut [&mut Candidate<'_, 'tcx>], + block: BasicBlock, + otherwise_block: &mut Option, + fake_borrows: &mut Option>>, + ) { + let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); + + if let PatKind::Or { .. } = *first_candidate.match_pairs[0].pattern.kind { + let match_pairs = mem::take(&mut first_candidate.match_pairs); + first_candidate.needs_otherwise_block = true; + first_candidate.pre_binding_block = Some(block); + + // We sort or-patterns to the end in `simplify_candidate`, so all + // the remaining match pairs are or-patterns. + for match_pair in match_pairs { + if let PatKind::Or { ref pats } = *match_pair.pattern.kind { + let or_span = match_pair.pattern.span; + let place = &match_pair.place; + + first_candidate.visit_leaves(|leaf_candidate| { + self.test_or_pattern(leaf_candidate, pats, or_span, place, fake_borrows); + }); + } else { + bug!("Or patterns should have been sorted to the end"); + } + } + let remainder_start = + first_candidate.otherwise_block.unwrap_or_else(|| self.cfg.start_new_block()); + self.match_candidates( + span, + remainder_start, + otherwise_block, + remaining_candidates, + fake_borrows, + ) + } else { + self.test_candidates(span, candidates, block, otherwise_block, fake_borrows) + } + } + + fn test_or_pattern<'pat>( + &mut self, + candidate: &mut Candidate<'pat, 'tcx>, + pats: &'pat [Pat<'tcx>], + or_span: Span, + place: &Place<'tcx>, + fake_borrows: &mut Option>>, + ) { + debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); + let mut or_candidates: Vec<_> = pats + .iter() + .map(|pat| { + let new_match_pair = smallvec![MatchPair { pattern: pat, place: place.clone() }]; + Candidate { + span: pat.span, + has_guard: candidate.has_guard, + needs_otherwise_block: candidate.needs_otherwise_block, + match_pairs: new_match_pair, + bindings: Vec::new(), + ascriptions: Vec::new(), + otherwise_block: None, + pre_binding_block: None, + next_candidate_pre_binding_block: None, + subcandidates: Vec::new(), + } + }) + .collect(); + let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); + self.match_candidates( + or_span, + candidate.pre_binding_block.unwrap(), + &mut candidate.otherwise_block, + &mut or_candidate_refs, + fake_borrows, + ); + candidate.subcandidates = or_candidates; + self.merge_trivial_subcandidates(candidate, self.source_info(or_span)); + } + + /// Try to merge all of the subcandidates of the given candidate into one. + /// This avoids exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. + fn merge_trivial_subcandidates( + &mut self, + candidate: &mut Candidate<'_, 'tcx>, + source_info: SourceInfo, + ) { + if candidate.subcandidates.is_empty() { + return; + } + let mut can_merge = !candidate.has_guard; + + // Not `Iterator::all` because we don't want to short-circuit. + for subcandidate in &mut candidate.subcandidates { + self.merge_trivial_subcandidates(subcandidate, source_info); + + // FIXME(or_patterns; matthewjasper) Try to be more aggressive here. + can_merge &= subcandidate.subcandidates.is_empty() + && subcandidate.bindings.is_empty() + && subcandidate.ascriptions.is_empty(); + } + + if can_merge { + let any_matches = self.cfg.start_new_block(); + for subcandidate in mem::take(&mut candidate.subcandidates) { + let or_block = subcandidate.pre_binding_block.unwrap(); + self.cfg.goto(or_block, source_info, any_matches); + } + candidate.pre_binding_block = Some(any_matches); + } + } + /// 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 @@ -1258,6 +1482,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_and_guard_matched_candidate<'pat>( &mut self, candidate: Candidate<'pat, 'tcx>, + parent_bindings: &[(Vec>, Vec>)], guard: Option<(&Guard<'tcx>, region::Scope)>, fake_borrows: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, @@ -1281,7 +1506,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block = fresh_block; } - self.ascribe_types(block, &candidate.ascriptions); + self.ascribe_types( + block, + parent_bindings + .iter() + .flat_map(|(_, ascriptions)| ascriptions) + .chain(&candidate.ascriptions), + ); // rust-lang/rust#27282: The `autoref` business deserves some // explanation here. @@ -1365,14 +1596,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // reference to that. if let Some((guard, region_scope)) = guard { let tcx = self.hir.tcx(); + let bindings = parent_bindings + .iter() + .flat_map(|(bindings, _)| bindings) + .chain(&candidate.bindings); - self.bind_matched_candidate_for_guard(block, &candidate.bindings); + self.bind_matched_candidate_for_guard(block, bindings.clone()); let guard_frame = GuardFrame { - locals: candidate - .bindings - .iter() - .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)) - .collect(), + locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), }; debug!("entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); @@ -1446,9 +1677,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // ``` // // and that is clearly not correct. - let by_value_bindings = candidate.bindings.iter().filter(|binding| { - if let BindingMode::ByValue = binding.binding_mode { true } else { false } - }); + let by_value_bindings = + parent_bindings + .iter() + .flat_map(|(bindings, _)| bindings) + .chain(&candidate.bindings) + .filter(|binding| { + if let BindingMode::ByValue = binding.binding_mode { true } else { false } + }); // Read all of the by reference bindings to ensure that the // place they refer to can't be modified by the guard. for binding in by_value_bindings.clone() { @@ -1460,18 +1696,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { post_guard_block } else { - assert!(candidate.otherwise_block.is_none()); // (Here, it is not too early to bind the matched // candidate on `block`, because there is no guard result // that we have to inspect before we bind them.) - self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); + self.bind_matched_candidate_for_arm_body( + block, + parent_bindings + .iter() + .flat_map(|(bindings, _)| bindings) + .chain(&candidate.bindings), + ); block } } /// Append `AscribeUserType` statements onto the end of `block` /// for each ascription - fn ascribe_types(&mut self, block: BasicBlock, ascriptions: &[Ascription<'tcx>]) { + fn ascribe_types<'b>( + &mut self, + block: BasicBlock, + ascriptions: impl IntoIterator>, + ) where + 'tcx: 'b, + { for ascription in ascriptions { let source_info = self.source_info(ascription.span); @@ -1498,14 +1745,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - fn bind_matched_candidate_for_guard(&mut self, block: BasicBlock, bindings: &[Binding<'tcx>]) { - debug!("bind_matched_candidate_for_guard(block={:?}, bindings={:?})", block, bindings); + fn bind_matched_candidate_for_guard<'b>( + &mut self, + block: BasicBlock, + bindings: impl IntoIterator>, + ) where + 'tcx: 'b, + { + debug!("bind_matched_candidate_for_guard(block={:?})", block); // Assign each of the bindings. Since we are binding for a // guard expression, this will never trigger moves out of the // candidate. let re_erased = self.hir.tcx().lifetimes.re_erased; for binding in bindings { + debug!("bind_matched_candidate_for_guard(binding={:?})", binding); let source_info = self.source_info(binding.span); // For each pattern ident P of type T, `ref_for_guard` is diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index 77bbce2d37aa9..3a806ab6bff0a 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -16,18 +16,40 @@ use crate::build::matches::{Ascription, Binding, Candidate, MatchPair}; use crate::build::Builder; use crate::hair::{self, *}; use rustc::mir::interpret::truncate; +use rustc::mir::Place; use rustc::ty; use rustc::ty::layout::{Integer, IntegerExt, Size}; use rustc_attr::{SignedInt, UnsignedInt}; use rustc_hir::RangeEnd; +use smallvec::smallvec; use std::mem; impl<'a, 'tcx> Builder<'a, 'tcx> { - crate 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`. + crate 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) { @@ -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> { + 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 diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs index 1f97f5f1b7281..ff95f16225751 100644 --- a/src/librustc_mir_build/build/matches/test.rs +++ b/src/librustc_mir_build/build/matches/test.rs @@ -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 { .. } diff --git a/src/librustc_mir_build/hair/mod.rs b/src/librustc_mir_build/hair/mod.rs index 0f2c76152edae..cb93ba7c9250f 100644 --- a/src/librustc_mir_build/hair/mod.rs +++ b/src/librustc_mir_build/hair/mod.rs @@ -315,17 +315,6 @@ crate struct Arm<'tcx> { crate span: Span, } -impl<'tcx> Arm<'tcx> { - // HACK(or_patterns; Centril | dlrobertson): Remove this and - // correctly handle each case in which this method is used. - crate fn top_pats_hack(&self) -> &[Pat<'tcx>] { - match &*self.pattern.kind { - PatKind::Or { pats } => pats, - _ => std::slice::from_ref(&self.pattern), - } - } -} - #[derive(Clone, Debug)] crate enum Guard<'tcx> { If(ExprRef<'tcx>), From a20969c489d7f415f8073aacef1d480de6459ce8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 17:31:21 +0000 Subject: [PATCH 05/13] Implement general or-patterns in `let` statements --- src/librustc_mir_build/build/matches/mod.rs | 107 ++++++++++---------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index 4ac3ced17dbb1..928363246c2c0 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -96,11 +96,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut arm_candidates = self.create_match_candidates(&scrutinee_place, &arms); let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); - let candidates = + let mut candidates = arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::>(); let fake_borrow_temps = - self.lower_match_tree(block, scrutinee_span, match_has_guard, candidates); + self.lower_match_tree(block, scrutinee_span, match_has_guard, &mut candidates); self.lower_match_arms( &destination, @@ -181,7 +181,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block: BasicBlock, scrutinee_span: Span, match_has_guard: bool, - mut candidates: Vec<&mut Candidate<'pat, 'tcx>>, + candidates: &mut [&mut Candidate<'pat, 'tcx>], ) -> Vec<(Place<'tcx>, Local)> { // The set of places that we are creating fake borrows of. If there are // no match guards then we don't need any fake borrows, so don't track @@ -192,13 +192,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // This will generate code to test scrutinee_place and // branch to the appropriate arm block - self.match_candidates( - scrutinee_span, - block, - &mut otherwise, - &mut candidates, - &mut fake_borrows, - ); + self.match_candidates(scrutinee_span, block, &mut otherwise, candidates, &mut fake_borrows); if let Some(otherwise_block) = otherwise { let source_info = self.source_info(scrutinee_span); @@ -207,7 +201,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; - for candidate in candidates.into_iter() { + for candidate in candidates { candidate.visit_leaves(|leaf_candidate| { if let Some(ref mut prev) = previous_candidate { prev.next_candidate_pre_binding_block = leaf_candidate.pre_binding_block; @@ -263,7 +257,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { arm.guard.as_ref().map(|g| (g, match_scope)), &fake_borrow_temps, scrutinee_span, - arm.scope, + Some(arm.scope), ); if let Some(source_scope) = scope { @@ -297,7 +291,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option<(&Guard<'tcx>, region::Scope)>, fake_borrow_temps: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, - arm_scope: region::Scope, + arm_scope: Option, ) -> BasicBlock { if candidate.subcandidates.is_empty() { // Avoid generating another `BasicBlock` when we only have one @@ -308,10 +302,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard, fake_borrow_temps, scrutinee_span, + true, ) } else { let target_block = self.cfg.start_new_block(); - + let mut schedule_drops = true; // We keep a stack of all of the bindings and type asciptions // from the the parent candidates that we visit, that also need to // be bound for each candidate. @@ -319,14 +314,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate, &mut Vec::new(), &mut |leaf_candidate, parent_bindings| { - self.clear_top_scope(arm_scope); + if let Some(arm_scope) = arm_scope { + // Avoid scheduling drops multiple times by unscheduling drops. + self.clear_top_scope(arm_scope); + } let binding_end = self.bind_and_guard_matched_candidate( leaf_candidate, parent_bindings, guard, &fake_borrow_temps, scrutinee_span, + schedule_drops, ); + if arm_scope.is_none() { + // If we aren't in a match, then our bindings may not be + // the only thing in the top scope, so only schedule + // them to drop for the first pattern instead. + schedule_drops = false; + } self.cfg.goto(binding_end, outer_source_info, target_block); }, |inner_candidate, parent_bindings| { @@ -460,51 +465,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { subcandidates: vec![], }; - // Simplify the candidate. Since the pattern is irrefutable, this should - // always convert all match-pairs into bindings. - self.simplify_candidate(&mut candidate); - - if !candidate.match_pairs.is_empty() { - // ICE if no other errors have been emitted. This used to be a hard error that wouldn't - // be reached because `hair::pattern::check_match::check_match` wouldn't have let the - // compiler continue. In our tests this is only ever hit by - // `ui/consts/const-match-check.rs` with `--cfg eval1`, and that file already generates - // a different error before hand. - self.hir.tcx().sess.delay_span_bug( - candidate.match_pairs[0].pattern.span, - &format!( - "match pairs {:?} remaining after simplifying irrefutable pattern", - candidate.match_pairs, - ), - ); - } + let fake_borrow_temps = + self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]); // for matches and function arguments, the place that is being matched // can be set when creating the variables. But the place for // let PATTERN = ... might not even exist until we do the assignment. // so we set it here instead if set_match_place { - for binding in &candidate.bindings { - let local = self.var_local_id(binding.var_id, OutsideGuard); - - if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(VarBindingForm { - opt_match_place: Some((ref mut match_place, _)), - .. - }))) = self.local_decls[local].local_info - { - *match_place = Some(*initializer); - } else { - bug!("Let binding to non-user variable.") + let mut candidate_ref = &candidate; + while let Some(next) = { + for binding in &candidate_ref.bindings { + let local = self.var_local_id(binding.var_id, OutsideGuard); + + if let LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var( + VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, + ))) = self.local_decls[local].local_info + { + *match_place = Some(*initializer); + } else { + bug!("Let binding to non-user variable.") + } } + candidate_ref.subcandidates.get(0) + } { + candidate_ref = next; } } - self.ascribe_types(block, &candidate.ascriptions); - - // now apply the bindings, which will also declare the variables - self.bind_matched_candidate_for_arm_body(block, &candidate.bindings); - - block.unit() + self.bind_pattern( + self.source_info(irrefutable_pat.span), + candidate, + None, + &fake_borrow_temps, + irrefutable_pat.span, + None, + ) + .unit() } /// Declares the bindings of the given patterns and returns the visibility @@ -1486,6 +1483,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { guard: Option<(&Guard<'tcx>, region::Scope)>, fake_borrows: &Vec<(Place<'tcx>, Local)>, scrutinee_span: Span, + schedule_drops: bool, ) -> BasicBlock { debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate); @@ -1692,7 +1690,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let cause = FakeReadCause::ForGuardBinding; self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id)); } - self.bind_matched_candidate_for_arm_body(post_guard_block, by_value_bindings); + assert!(schedule_drops, "patterns with guards must schedule drops"); + self.bind_matched_candidate_for_arm_body(post_guard_block, true, by_value_bindings); post_guard_block } else { @@ -1701,6 +1700,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // that we have to inspect before we bind them.) self.bind_matched_candidate_for_arm_body( block, + schedule_drops, parent_bindings .iter() .flat_map(|(bindings, _)| bindings) @@ -1793,6 +1793,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_matched_candidate_for_arm_body<'b>( &mut self, block: BasicBlock, + schedule_drops: bool, bindings: impl IntoIterator>, ) where 'tcx: 'b, @@ -1805,7 +1806,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = self.source_info(binding.span); let local = self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard); - self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); + if schedule_drops { + self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); + } let rvalue = match binding.binding_mode { BindingMode::ByValue => { Rvalue::Use(self.consume_by_copy_or_move(binding.source.clone())) From 30058df867fbe5c43f90707d6fb644fba6201c2a Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 17:53:00 +0000 Subject: [PATCH 06/13] Update existing tests for or-patterns --- src/test/mir-opt/const_prop/discriminant.rs | 20 +-- src/test/mir-opt/issue-62289.rs | 38 +++--- src/test/mir-opt/match-arm-scopes.rs | 110 ++++++++-------- src/test/mir-opt/match_false_edges.rs | 118 +++++++++--------- src/test/mir-opt/match_test.rs | 28 ++--- src/test/mir-opt/remove_fake_borrows.rs | 24 ++-- src/test/mir-opt/simplify_try.rs | 40 +++--- src/test/ui/consts/const_let_refutable.rs | 8 +- src/test/ui/consts/const_let_refutable.stderr | 28 ++--- src/test/ui/empty/empty-never-array.rs | 1 - src/test/ui/empty/empty-never-array.stderr | 11 +- src/test/ui/issues/issue-12567.stderr | 4 +- src/test/ui/issues/issue-15381.rs | 1 - src/test/ui/issues/issue-15381.stderr | 11 +- .../ui/or-patterns/consistent-bindings.rs | 46 ++++--- .../ui/or-patterns/consistent-bindings.stderr | 9 -- .../exhaustiveness-non-exhaustive.rs | 7 +- .../exhaustiveness-non-exhaustive.stderr | 14 +-- .../ui/or-patterns/exhaustiveness-pass.rs | 9 +- .../ui/or-patterns/exhaustiveness-pass.stderr | 8 -- .../exhaustiveness-unreachable-pattern.rs | 7 +- .../exhaustiveness-unreachable-pattern.stderr | 48 ++++--- .../ui/or-patterns/feature-gate-const-fn.rs | 2 + .../or-patterns/feature-gate-const-fn.stderr | 17 ++- .../recursive-types-are-not-uninhabited.rs | 1 - ...recursive-types-are-not-uninhabited.stderr | 11 +- .../duplicate-suggestions.stderr | 30 +++-- .../dont-suggest-ref/simple.stderr | 51 +++++--- 28 files changed, 332 insertions(+), 370 deletions(-) delete mode 100644 src/test/ui/or-patterns/consistent-bindings.stderr delete mode 100644 src/test/ui/or-patterns/exhaustiveness-pass.stderr diff --git a/src/test/mir-opt/const_prop/discriminant.rs b/src/test/mir-opt/const_prop/discriminant.rs index 07bbd9202b940..667d21fc14ee4 100644 --- a/src/test/mir-opt/const_prop/discriminant.rs +++ b/src/test/mir-opt/const_prop/discriminant.rs @@ -10,18 +10,18 @@ fn main() { // ... // _3 = std::option::Option::::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); @@ -33,18 +33,18 @@ fn main() { // ... // _3 = const Scalar(0x01) : std::option::Option; // _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); diff --git a/src/test/mir-opt/issue-62289.rs b/src/test/mir-opt/issue-62289.rs index a3b517e9bca87..8e619ffdf8b96 100644 --- a/src/test/mir-opt/issue-62289.rs +++ b/src/test/mir-opt/issue-62289.rs @@ -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 >::from(move _9) -> [return: bb7, unwind: bb3]; +// _8 = const >::from(move _9) -> [return: bb8, unwind: bb3]; // } -// bb6: { +// bb7: { // return; // } -// bb7: { +// bb8: { // StorageDead(_9); -// _0 = const > as std::ops::Try>::from_error(move _8) -> [return: bb8, unwind: bb3]; +// _0 = const > 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; @@ -85,7 +85,7 @@ fn main() { // bb13: { // StorageDead(_1); // StorageDead(_3); -// goto -> bb6; +// goto -> bb7; // } // } // END rustc.test.ElaborateDrops.before.mir diff --git a/src/test/mir-opt/match-arm-scopes.rs b/src/test/mir-opt/match-arm-scopes.rs index 4412a16e74d5e..7afc3bbd6fae8 100644 --- a/src/test/mir-opt/match-arm-scopes.rs +++ b/src/test/mir-opt/match-arm-scopes.rs @@ -28,10 +28,7 @@ const CASES: &[(bool, bool, bool, i32)] = &[ fn main() { for &(cond, items_1, items_2, result) in CASES { - assert_eq!( - complicated_match(cond, (items_1, items_2, String::new())), - result, - ); + assert_eq!(complicated_match(cond, (items_1, items_2, String::new())), result,); } } @@ -64,31 +61,38 @@ fn main() { // } // bb0: { // FakeRead(ForMatchedPlace, _2); -// switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb5]; +// switchInt((_2.0: bool)) -> [false: bb2, otherwise: bb3]; // } // bb1 (cleanup): { // resume; // } -// bb2: { -// falseEdges -> [real: bb8, imaginary: bb3]; +// bb2: { // pre-binding for arm 1 first pattern +// falseEdges -> [real: bb9, imaginary: bb4]; // } // bb3: { -// falseEdges -> [real: bb17, imaginary: bb4]; +// switchInt((_2.1: bool)) -> [false: bb4, otherwise: bb5]; // } -// bb4: { -// falseEdges -> [real: bb25, imaginary: bb26]; +// bb4: { // pre-binding for arm 1 second pattern +// falseEdges -> [real: bb18, imaginary: bb6]; // } // bb5: { -// switchInt((_2.1: bool)) -> [false: bb3, otherwise: bb6]; +// switchInt((_2.0: bool)) -> [false: bb7, otherwise: bb6]; // } -// bb6: { -// switchInt((_2.0: bool)) -> [false: bb26, otherwise: bb4]; +// bb6: { // pre-binding for arm 2 first pattern +// falseEdges -> [real: bb26, imaginary: bb7]; // } -// bb7: { // arm 1 +// bb7: { // bindings for arm 2 - second pattern +// StorageLive(_15); +// _15 = (_2.1: bool); +// StorageLive(_16); +// _16 = move (_2.2: std::string::String); +// goto -> bb25; +// } +// bb8: { // arm 1 // _0 = const 1i32; -// drop(_7) -> [return: bb23, unwind: bb13]; +// drop(_7) -> [return: bb24, unwind: bb14]; // } -// bb8: { // guard - first time +// bb9: { // guard - first time // StorageLive(_6); // _6 = &(_2.1: bool); // StorageLive(_8); @@ -99,34 +103,34 @@ fn main() { // StorageLive(_10); // _10 = _1; // FakeRead(ForMatchedPlace, _10); -// switchInt(_10) -> [false: bb10, otherwise: bb9]; +// switchInt(_10) -> [false: bb11, otherwise: bb10]; // } -// bb9: { -// falseEdges -> [real: bb11, imaginary: bb10]; +// bb10: { +// falseEdges -> [real: bb12, imaginary: bb11]; // } -// bb10: { // `else` block - first time +// bb11: { // `else` block - first time // _9 = (*_6); // StorageDead(_10); -// switchInt(move _9) -> [false: bb16, otherwise: bb15]; +// switchInt(move _9) -> [false: bb17, otherwise: bb16]; // } -// bb11: { // `return 3` - first time +// bb12: { // `return 3` - first time // _0 = const 3i32; // StorageDead(_10); // StorageDead(_9); // StorageDead(_8); // StorageDead(_6); -// goto -> bb14; +// goto -> bb15; // } -// bb12: { +// bb13: { // return; // } -// bb13 (cleanup): { +// bb14 (cleanup): { // drop(_2) -> bb1; // } -// bb14: { -// drop(_2) -> [return: bb12, unwind: bb1]; -// } // bb15: { +// drop(_2) -> [return: bb13, unwind: bb1]; +// } +// bb16: { // StorageDead(_9); // FakeRead(ForMatchGuard, _3); // FakeRead(ForMatchGuard, _4); @@ -136,15 +140,15 @@ fn main() { // _5 = (_2.1: bool); // StorageLive(_7); // _7 = move (_2.2: std::string::String); -// goto -> bb7; +// goto -> bb8; // } -// bb16: { // guard otherwise case - first time +// bb17: { // guard otherwise case - first time // StorageDead(_9); // StorageDead(_8); // StorageDead(_6); -// falseEdges -> [real: bb5, imaginary: bb3]; +// falseEdges -> [real: bb3, imaginary: bb4]; // } -// bb17: { // guard - second time +// bb18: { // guard - second time // StorageLive(_6); // _6 = &(_2.0: bool); // StorageLive(_8); @@ -155,25 +159,25 @@ fn main() { // StorageLive(_13); // _13 = _1; // FakeRead(ForMatchedPlace, _13); -// switchInt(_13) -> [false: bb19, otherwise: bb18]; +// switchInt(_13) -> [false: bb20, otherwise: bb19]; // } -// bb18: { -// falseEdges -> [real: bb20, imaginary: bb19]; +// bb19: { +// falseEdges -> [real: bb21, imaginary: bb20]; // } -// bb19: { // `else` block - second time +// bb20: { // `else` block - second time // _12 = (*_6); // StorageDead(_13); -// switchInt(move _12) -> [false: bb22, otherwise: bb21]; +// switchInt(move _12) -> [false: bb23, otherwise: bb22]; // } -// bb20: { +// bb21: { // _0 = const 3i32; // StorageDead(_13); // StorageDead(_12); // StorageDead(_8); // StorageDead(_6); -// goto -> bb14; +// goto -> bb15; // } -// bb21: { // bindings for arm 1 +// bb22: { // bindings for arm 1 // StorageDead(_12); // FakeRead(ForMatchGuard, _3); // FakeRead(ForMatchGuard, _4); @@ -183,46 +187,40 @@ fn main() { // _5 = (_2.0: bool); // StorageLive(_7); // _7 = move (_2.2: std::string::String); -// goto -> bb7; +// goto -> bb8; // } -// bb22: { // Guard otherwise case - second time +// bb23: { // Guard otherwise case - second time // StorageDead(_12); // StorageDead(_8); // StorageDead(_6); -// falseEdges -> [real: bb6, imaginary: bb4]; +// falseEdges -> [real: bb5, imaginary: bb6]; // } -// bb23: { // rest of arm 1 +// bb24: { // rest of arm 1 // StorageDead(_7); // StorageDead(_5); // StorageDead(_8); // StorageDead(_6); // goto -> bb28; // } -// bb24: { // arm 2 +// bb25: { // arm 2 // _0 = const 2i32; -// drop(_16) -> [return: bb27, unwind: bb13]; +// drop(_16) -> [return: bb27, unwind: bb14]; // } -// bb25: { // bindings for arm 2 - first pattern +// bb26: { // bindings for arm 2 - first pattern // StorageLive(_15); // _15 = (_2.1: bool); // StorageLive(_16); // _16 = move (_2.2: std::string::String); -// goto -> bb24; -// } -// bb26: { // bindings for arm 2 - second pattern -// StorageLive(_15); -// _15 = (_2.1: bool); -// StorageLive(_16); -// _16 = move (_2.2: std::string::String); -// goto -> bb24; +// goto -> bb25; // } + // bb27: { // rest of arm 2 // StorageDead(_16); // StorageDead(_15); // goto -> bb28; // } // bb28: { -// drop(_2) -> [return: bb12, unwind: bb1]; +// drop(_2) -> [return: bb13, unwind: bb1]; // } // END rustc.complicated_match.SimplifyCfg-initial.after.mir // START rustc.complicated_match.ElaborateDrops.after.mir diff --git a/src/test/mir-opt/match_false_edges.rs b/src/test/mir-opt/match_false_edges.rs index 2c20c35e4a491..237828d9020db 100644 --- a/src/test/mir-opt/match_false_edges.rs +++ b/src/test/mir-opt/match_false_edges.rs @@ -4,7 +4,7 @@ fn guard() -> bool { false } -fn guard2(_:i32) -> bool { +fn guard2(_: i32) -> bool { true } @@ -45,20 +45,20 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb4, 1isize: bb2, otherwise: bb5]; +// switchInt(move _3) -> [0isize: bb2, 1isize: bb3, otherwise: bb5]; // } // bb1 (cleanup): { // resume; // } -// bb2: { -// falseEdges -> [real: bb6, imaginary: bb3]; //pre_binding1 +// bb2: { // pre_binding3 and arm3 +// _1 = (const 3i32, const 3i32); +// goto -> bb11; // } // bb3: { -// falseEdges -> [real: bb10, imaginary: bb4]; //pre_binding2 +// falseEdges -> [real: bb6, imaginary: bb4]; //pre_binding1 // } -// bb4: { //pre_binding3 and arm3 -// _1 = (const 3i32, const 3i32); -// goto -> bb11; +// bb4: { +// falseEdges -> [real: bb10, imaginary: bb2]; //pre_binding2 // } // bb5: { // unreachable; @@ -91,7 +91,7 @@ fn main() { // bb9: { // to pre_binding2 // StorageDead(_7); // StorageDead(_6); -// goto -> bb3; +// goto -> bb4; // } // bb10: { // arm2 // StorageLive(_9); @@ -103,7 +103,7 @@ fn main() { // StorageDead(_9); // goto -> bb11; // } -// bb11: { // arm3 +// bb11: { // StorageDead(_2); // StorageDead(_1); // _0 = (); @@ -117,31 +117,41 @@ fn main() { // _2 = std::option::Option::::Some(const 42i32,); // FakeRead(ForMatchedPlace, _2); // _3 = discriminant(_2); -// switchInt(move _3) -> [0isize: bb3, 1isize: bb2, otherwise: bb4]; +// switchInt(move _3) -> [0isize: bb2, 1isize: bb3, otherwise: bb5]; // } // bb1 (cleanup): { // resume; // } -// bb2: { -// falseEdges -> [real: bb5, imaginary: bb3]; +// bb2: { // pre_binding2 +// falseEdges -> [real: bb10, imaginary: bb4]; // } -// bb3: { -// falseEdges -> [real: bb9, imaginary: bb10]; +// bb3: { // pre_binding1 +// falseEdges -> [real: bb6, imaginary: bb2]; // } -// bb4: { // to arm3 (can skip 2 since this is `Some`) +// bb4: { // binding3 and arm3 +// StorageLive(_9); +// _9 = ((_2 as Some).0: i32); +// StorageLive(_10); +// _10 = _9; +// _1 = (const 2i32, move _10); +// StorageDead(_10); +// StorageDead(_9); +// goto -> bb11; +// } +// bb5: { // unreachable; // } -// bb5: { // binding1 and guard +// bb6: { // StorageLive(_6); // _6 = &((_2 as Some).0: i32); // _4 = &shallow _2; // StorageLive(_7); -// _7 = const guard() -> [return: bb6, unwind: bb1]; +// _7 = const guard() -> [return: bb7, unwind: bb1]; // } -// bb6: { // end of guard -// switchInt(move _7) -> [false: bb8, otherwise: bb7]; +// bb7: { // end of guard +// switchInt(move _7) -> [false: bb9, otherwise: bb8]; // } -// bb7: { +// bb8: { // StorageDead(_7); // FakeRead(ForMatchGuard, _4); // FakeRead(ForGuardBinding, _6); @@ -155,25 +165,15 @@ fn main() { // StorageDead(_6); // goto -> bb11; // } -// bb8: { // to pre_binding3 (can skip 2 since this is `Some`) +// bb9: { // to pre_binding3 (can skip 2 since this is `Some`) // StorageDead(_7); // StorageDead(_6); -// falseEdges -> [real: bb10, imaginary: bb3]; +// falseEdges -> [real: bb4, imaginary: bb2]; // } -// bb9: { // arm2 +// bb10: { // arm2 // _1 = (const 3i32, const 3i32); // goto -> bb11; // } -// bb10: { // binding3 and arm3 -// StorageLive(_9); -// _9 = ((_2 as Some).0: i32); -// StorageLive(_10); -// _10 = _9; -// _1 = (const 2i32, move _10); -// StorageDead(_10); -// StorageDead(_9); -// goto -> bb11; -// } // bb11: { // StorageDead(_2); // StorageDead(_1); @@ -188,31 +188,38 @@ fn main() { // _2 = std::option::Option::::Some(const 1i32,); // FakeRead(ForMatchedPlace, _2); // _4 = discriminant(_2); -// switchInt(move _4) -> [1isize: bb2, otherwise: bb3]; +// switchInt(move _4) -> [1isize: bb3, otherwise: bb2]; // } // bb1 (cleanup): { // resume; // } // bb2: { -// falseEdges -> [real: bb5, imaginary: bb3]; +// falseEdges -> [real: bb10, imaginary: bb5]; // } // bb3: { -// falseEdges -> [real: bb9, imaginary: bb4]; +// falseEdges -> [real: bb6, imaginary: bb2]; // } // bb4: { -// falseEdges -> [real: bb10, imaginary: bb14]; +// StorageLive(_14); +// _14 = _2; +// _1 = const 4i32; +// StorageDead(_14); +// goto -> bb15; // } // bb5: { +// falseEdges -> [real: bb11, imaginary: bb4]; +// } +// bb6: { //end of guard1 // StorageLive(_7); // _7 = &((_2 as Some).0: i32); // _5 = &shallow _2; // StorageLive(_8); -// _8 = const guard() -> [return: bb6, unwind: bb1]; -// } -// bb6: { //end of guard1 -// switchInt(move _8) -> [false: bb8, otherwise: bb7]; +// _8 = const guard() -> [return: bb7, unwind: bb1]; // } // bb7: { +// switchInt(move _8) -> [false: bb9, otherwise: bb8]; +// } +// bb8: { // StorageDead(_8); // FakeRead(ForMatchGuard, _5); // FakeRead(ForGuardBinding, _7); @@ -223,32 +230,32 @@ fn main() { // StorageDead(_7); // goto -> bb15; // } -// bb8: { +// bb9: { // StorageDead(_8); // StorageDead(_7); -// falseEdges -> [real: bb3, imaginary: bb3]; +// falseEdges -> [real: bb2, imaginary: bb2]; // } -// bb9: { // binding2 & arm2 +// bb10: { // binding2 & arm2 // StorageLive(_9); // _9 = _2; // _1 = const 2i32; // StorageDead(_9); // goto -> bb15; // } -// bb10: { // binding3: Some(y) if guard2(y) +// bb11: { // binding3: Some(y) if guard2(y) // StorageLive(_11); // _11 = &((_2 as Some).0: i32); // _5 = &shallow _2; // StorageLive(_12); // StorageLive(_13); // _13 = (*_11); -// _12 = const guard2(move _13) -> [return: bb11, unwind: bb1]; +// _12 = const guard2(move _13) -> [return: bb12, unwind: bb1]; // } -// bb11: { // end of guard2 +// bb12: { // end of guard2 // StorageDead(_13); -// switchInt(move _12) -> [false: bb13, otherwise: bb12]; +// switchInt(move _12) -> [false: bb14, otherwise: bb13]; // } -// bb12: { // binding4 & arm4 +// bb13: { // binding4 & arm4 // StorageDead(_12); // FakeRead(ForMatchGuard, _5); // FakeRead(ForGuardBinding, _11); @@ -259,17 +266,10 @@ fn main() { // StorageDead(_11); // goto -> bb15; // } -// bb13: { +// bb14: { // StorageDead(_12); // StorageDead(_11); -// falseEdges -> [real: bb14, imaginary: bb14]; -// } -// bb14: { -// StorageLive(_14); -// _14 = _2; -// _1 = const 4i32; -// StorageDead(_14); -// goto -> bb15; +// falseEdges -> [real: bb4, imaginary: bb4]; // } // bb15: { // StorageDead(_2); diff --git a/src/test/mir-opt/match_test.rs b/src/test/mir-opt/match_test.rs index 1ca75b100410b..5ee3e1447d832 100644 --- a/src/test/mir-opt/match_test.rs +++ b/src/test/mir-opt/match_test.rs @@ -20,35 +20,35 @@ fn main() { // START rustc.main.SimplifyCfg-initial.after.mir // bb0: { // ... -// switchInt(move _6) -> [false: bb6, otherwise: bb5]; +// switchInt(move _6) -> [false: bb4, otherwise: bb1]; // } // bb1: { -// falseEdges -> [real: bb9, imaginary: bb2]; +// _7 = Lt(_1, const 10i32); +// switchInt(move _7) -> [false: bb4, otherwise: bb2]; // } // bb2: { -// falseEdges -> [real: bb12, imaginary: bb3]; +// falseEdges -> [real: bb9, imaginary: bb6]; // } // bb3: { -// falseEdges -> [real: bb13, imaginary: bb4]; -// } -// bb4: { // _3 = const 3i32; // goto -> bb14; // } +// bb4: { +// _4 = Le(const 10i32, _1); +// switchInt(move _4) -> [false: bb7, otherwise: bb5]; +// } // bb5: { -// _7 = Lt(_1, const 10i32); -// switchInt(move _7) -> [false: bb6, otherwise: bb1]; +// _5 = Le(_1, const 20i32); +// switchInt(move _5) -> [false: bb7, otherwise: bb6]; // } // bb6: { -// _4 = Le(const 10i32, _1); -// switchInt(move _4) -> [false: bb8, otherwise: bb7]; +// falseEdges -> [real: bb12, imaginary: bb8]; // } // bb7: { -// _5 = Le(_1, const 20i32); -// switchInt(move _5) -> [false: bb8, otherwise: bb2]; +// switchInt(_1) -> [-1i32: bb8, otherwise: bb3]; // } // bb8: { -// switchInt(_1) -> [-1i32: bb3, otherwise: bb4]; +// falseEdges -> [real: bb13, imaginary: bb3]; // } // bb9: { // _8 = &shallow _1; @@ -64,7 +64,7 @@ fn main() { // } // bb11: { // StorageDead(_9); -// falseEdges -> [real: bb4, imaginary: bb2]; +// falseEdges -> [real: bb3, imaginary: bb6]; // } // bb12: { // _3 = const 1i32; diff --git a/src/test/mir-opt/remove_fake_borrows.rs b/src/test/mir-opt/remove_fake_borrows.rs index 965897ad541e7..294fe247c38be 100644 --- a/src/test/mir-opt/remove_fake_borrows.rs +++ b/src/test/mir-opt/remove_fake_borrows.rs @@ -19,17 +19,17 @@ fn main() { // bb0: { // FakeRead(ForMatchedPlace, _1); // _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb3, otherwise: bb2]; +// switchInt(move _3) -> [1isize: bb2, otherwise: bb1]; // } // bb1: { -// goto -> bb4; -// } -// bb2: { // _0 = const 1i32; // goto -> bb7; // } +// bb2: { +// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb3, otherwise: bb1]; +// } // bb3: { -// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; +// goto -> bb4; // } // bb4: { // _4 = &shallow _1; @@ -51,7 +51,7 @@ fn main() { // } // bb6: { // StorageDead(_8); -// goto -> bb2; +// goto -> bb1; // } // bb7: { // return; @@ -65,17 +65,17 @@ fn main() { // bb0: { // nop; // _3 = discriminant(_1); -// switchInt(move _3) -> [1isize: bb3, otherwise: bb2]; +// switchInt(move _3) -> [1isize: bb2, otherwise: bb1]; // } // bb1: { -// goto -> bb4; -// } -// bb2: { // _0 = const 1i32; // goto -> bb7; // } +// bb2: { +// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb3, otherwise: bb1]; +// } // bb3: { -// switchInt((*(*((_1 as Some).0: &' &' i32)))) -> [0i32: bb1, otherwise: bb2]; +// goto -> bb4; // } // bb4: { // nop; @@ -97,7 +97,7 @@ fn main() { // } // bb6: { // StorageDead(_8); -// goto -> bb2; +// goto -> bb1; // } // bb7: { // return; diff --git a/src/test/mir-opt/simplify_try.rs b/src/test/mir-opt/simplify_try.rs index d85eff45b4989..abac66d95c548 100644 --- a/src/test/mir-opt/simplify_try.rs +++ b/src/test/mir-opt/simplify_try.rs @@ -47,22 +47,22 @@ fn main() { // } // bb0: { // _5 = discriminant(_1); -// switchInt(move _5) -> [0isize: bb3, otherwise: bb1]; +// switchInt(move _5) -> [0isize: bb1, otherwise: bb2]; // } // bb1: { +// _10 = ((_1 as Ok).0: u32); +// ((_0 as Ok).0: u32) = move _10; +// discriminant(_0) = 0; +// goto -> bb3; +// } +// bb2: { // _6 = ((_1 as Err).0: i32); // ((_0 as Err).0: i32) = move _6; // discriminant(_0) = 1; -// goto -> bb2; -// } -// bb2: { -// return; +// goto -> bb3; // } // bb3: { -// _10 = ((_1 as Ok).0: u32); -// ((_0 as Ok).0: u32) = move _10; -// discriminant(_0) = 0; -// goto -> bb2; +// return; // } // } // END rustc.try_identity.SimplifyArmIdentity.before.mir @@ -106,22 +106,22 @@ fn main() { // } // bb0: { // _5 = discriminant(_1); -// switchInt(move _5) -> [0isize: bb3, otherwise: bb1]; +// switchInt(move _5) -> [0isize: bb1, otherwise: bb2]; // } // bb1: { // _0 = move _1; // nop; // nop; -// goto -> bb2; +// goto -> bb3; // } // bb2: { -// return; -// } -// bb3: { // _0 = move _1; // nop; // nop; -// goto -> bb2; +// goto -> bb3; +// } +// bb3: { +// return; // } // } // END rustc.try_identity.SimplifyArmIdentity.after.mir @@ -165,16 +165,16 @@ fn main() { // } // bb0: { // _5 = discriminant(_1); -// goto -> bb2; +// goto -> bb1; // } // bb1: { -// return; -// } -// bb2: { // _0 = move _1; // nop; // nop; -// goto -> bb1; +// goto -> bb2; +// } +// bb2: { +// return; // } // } // END rustc.try_identity.SimplifyBranchSame.after.mir diff --git a/src/test/ui/consts/const_let_refutable.rs b/src/test/ui/consts/const_let_refutable.rs index d48d5945e7da9..e49d47673912a 100644 --- a/src/test/ui/consts/const_let_refutable.rs +++ b/src/test/ui/consts/const_let_refutable.rs @@ -1,7 +1,7 @@ fn main() {} -const fn slice([a, b]: &[i32]) -> i32 { //~ ERROR refutable pattern in function argument - a + b //~ ERROR can only call other `const fn` within a `const fn` - //~^ ERROR use of possibly-uninitialized variable: `a` - //~| ERROR use of possibly-uninitialized variable: `b` +const fn slice(&[a, b]: &[i32]) -> i32 { + //~^ ERROR refutable pattern in function argument + //~| ERROR loops and conditional expressions are not stable in const fn + a + b } diff --git a/src/test/ui/consts/const_let_refutable.stderr b/src/test/ui/consts/const_let_refutable.stderr index 9acb4ad9cbbe5..719e14005ffa4 100644 --- a/src/test/ui/consts/const_let_refutable.stderr +++ b/src/test/ui/consts/const_let_refutable.stderr @@ -1,31 +1,19 @@ error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _, ..]` not covered --> $DIR/const_let_refutable.rs:3:16 | -LL | const fn slice([a, b]: &[i32]) -> i32 { - | ^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not covered +LL | const fn slice(&[a, b]: &[i32]) -> i32 { + | ^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not covered -error[E0723]: can only call other `const fn` within a `const fn`, but `const <&i32 as std::ops::Add>::add` is not stable as `const fn` - --> $DIR/const_let_refutable.rs:4:5 +error[E0723]: loops and conditional expressions are not stable in const fn + --> $DIR/const_let_refutable.rs:3:17 | -LL | a + b - | ^^^^^ +LL | const fn slice(&[a, b]: &[i32]) -> i32 { + | ^^^^^^ | = note: for more information, see issue https://github.com/rust-lang/rust/issues/57563 = help: add `#![feature(const_fn)]` to the crate attributes to enable -error[E0381]: use of possibly-uninitialized variable: `a` - --> $DIR/const_let_refutable.rs:4:5 - | -LL | a + b - | ^ use of possibly-uninitialized `a` - -error[E0381]: use of possibly-uninitialized variable: `b` - --> $DIR/const_let_refutable.rs:4:9 - | -LL | a + b - | ^ use of possibly-uninitialized `b` - -error: aborting due to 4 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0005, E0381, E0723. +Some errors have detailed explanations: E0005, E0723. For more information about an error, try `rustc --explain E0005`. diff --git a/src/test/ui/empty/empty-never-array.rs b/src/test/ui/empty/empty-never-array.rs index f0ecea42f39c8..01b99134a445f 100644 --- a/src/test/ui/empty/empty-never-array.rs +++ b/src/test/ui/empty/empty-never-array.rs @@ -10,7 +10,6 @@ fn transmute(t: T) -> U { let Helper::U(u) = Helper::T(t, []); //~^ ERROR refutable pattern in local binding: `T(_, _)` not covered u - //~^ ERROR use of possibly-uninitialized variable: `u` } fn main() { diff --git a/src/test/ui/empty/empty-never-array.stderr b/src/test/ui/empty/empty-never-array.stderr index d865b59f0b945..a4ffceea4c97f 100644 --- a/src/test/ui/empty/empty-never-array.stderr +++ b/src/test/ui/empty/empty-never-array.stderr @@ -19,13 +19,6 @@ help: you might want to use `if let` to ignore the variant that isn't matched LL | if let Helper::U(u) = Helper::T(t, []) { /* */ } | -error[E0381]: use of possibly-uninitialized variable: `u` - --> $DIR/empty-never-array.rs:12:5 - | -LL | u - | ^ use of possibly-uninitialized `u` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0005, E0381. -For more information about an error, try `rustc --explain E0005`. +For more information about this error, try `rustc --explain E0005`. diff --git a/src/test/ui/issues/issue-12567.stderr b/src/test/ui/issues/issue-12567.stderr index 2a88d8f0524ac..3ce659ccd14da 100644 --- a/src/test/ui/issues/issue-12567.stderr +++ b/src/test/ui/issues/issue-12567.stderr @@ -8,7 +8,7 @@ LL | (&[], &[hd, ..]) | (&[hd, ..], &[]) | -- data moved here LL | => println!("one empty"), LL | (&[hd1, ..], &[hd2, ..]) - | --- ...and here + | --- ...and here | = note: move occurs because these variables have types that don't implement the `Copy` trait @@ -22,7 +22,7 @@ LL | (&[], &[hd, ..]) | (&[hd, ..], &[]) | -- data moved here LL | => println!("one empty"), LL | (&[hd1, ..], &[hd2, ..]) - | --- ...and here + | --- ...and here | = note: move occurs because these variables have types that don't implement the `Copy` trait diff --git a/src/test/ui/issues/issue-15381.rs b/src/test/ui/issues/issue-15381.rs index 5307153cb4403..392fb1b24ddc4 100644 --- a/src/test/ui/issues/issue-15381.rs +++ b/src/test/ui/issues/issue-15381.rs @@ -4,6 +4,5 @@ fn main() { for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { //~^ ERROR refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` and 1 more not println!("y={}", y); - //~^ ERROR borrow of possibly-uninitialized variable: `y` } } diff --git a/src/test/ui/issues/issue-15381.stderr b/src/test/ui/issues/issue-15381.stderr index 47a0d514ad873..35f46ab57279c 100644 --- a/src/test/ui/issues/issue-15381.stderr +++ b/src/test/ui/issues/issue-15381.stderr @@ -4,13 +4,6 @@ error[E0005]: refutable pattern in `for` loop binding: `&[]`, `&[_]`, `&[_, _]` LL | for &[x,y,z] in values.chunks(3).filter(|&xs| xs.len() == 3) { | ^^^^^^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 1 more not covered -error[E0381]: borrow of possibly-uninitialized variable: `y` - --> $DIR/issue-15381.rs:6:26 - | -LL | println!("y={}", y); - | ^ use of possibly-uninitialized `y` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0005, E0381. -For more information about an error, try `rustc --explain E0005`. +For more information about this error, try `rustc --explain E0005`. diff --git a/src/test/ui/or-patterns/consistent-bindings.rs b/src/test/ui/or-patterns/consistent-bindings.rs index ec71afed872ba..3ee57978bb009 100644 --- a/src/test/ui/or-patterns/consistent-bindings.rs +++ b/src/test/ui/or-patterns/consistent-bindings.rs @@ -2,6 +2,8 @@ // edition:2018 +// check-pass + #![feature(or_patterns)] fn main() { @@ -11,35 +13,29 @@ fn main() { let Ok(ref mut a) | Err(ref mut a) = Ok(0); // Two levels: - enum Tri { V1(S), V2(T), V3(U) } + enum Tri { + V1(S), + V2(T), + V3(U), + } use Tri::*; - let Ok((V1(a) | V2(a) | V3(a), b)) | Err(Ok((a, b)) | Err((a, b))) - : Result<_, Result<_, _>> - = Ok((V1(1), 1)); + let Ok((V1(a) | V2(a) | V3(a), b)) | Err(Ok((a, b)) | Err((a, b))): Result<_, Result<_, _>> = + Ok((V1(1), 1)); - let Ok((V1(a) | V2(a) | V3(a), ref b)) | Err(Ok((a, ref b)) | Err((a, ref b))) - : Result<_, Result<_, _>> - = Ok((V1(1), 1)); + let Ok((V1(a) | V2(a) | V3(a), ref b)) | Err(Ok((a, ref b)) | Err((a, ref b))): Result< + _, + Result<_, _>, + > = Ok((V1(1), 1)); // Three levels: let ( - a, - Err((ref mut b, ref c, d)) | - Ok(( - Ok( - V1((ref c, d)) | - V2((d, ref c)) | - V3((ref c, Ok((_, d)) | Err((d, _)))) - ) | - Err((ref c, d)), - ref mut b - )) - ) = - (1, Ok((Ok(V3((1, Ok((1, 1))))), 1))); - - // FIXME(or_patterns; Centril | dlrobertson): remove this line below and - // change this test to check-pass once MIR can handle or-patterns with bindings. - let () = 0; - //~^ ERROR mismatched types + a, + Err((ref mut b, ref c, d)) + | Ok(( + Ok(V1((ref c, d)) | V2((d, ref c)) | V3((ref c, Ok((_, d)) | Err((d, _))))) + | Err((ref c, d)), + ref mut b, + )), + ): (_, Result<_, _>) = (1, Ok((Ok(V3((1, Ok::<_, (i32, i32)>((1, 1))))), 1))); } diff --git a/src/test/ui/or-patterns/consistent-bindings.stderr b/src/test/ui/or-patterns/consistent-bindings.stderr deleted file mode 100644 index bb8e90af5f202..0000000000000 --- a/src/test/ui/or-patterns/consistent-bindings.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/consistent-bindings.rs:43:9 - | -LL | let () = 0; - | ^^ expected integer, found `()` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs index 2e8baf978e251..c8bc4a2a8d51b 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.rs @@ -1,13 +1,8 @@ #![feature(or_patterns)] #![deny(unreachable_patterns)] -// We wrap patterns in a tuple because top-level or-patterns are special-cased for now. +// We wrap patterns in a tuple because top-level or-patterns were special-cased. fn main() { - // Get the fatal error out of the way - match (0u8,) { - (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet - } - match (0u8, 0u8) { //~^ ERROR non-exhaustive patterns: `(2u8..=std::u8::MAX, _)` (0 | 1, 2 | 3) => {} diff --git a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr index 7fbd846a22f2b..3ba26de10d3d5 100644 --- a/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-non-exhaustive.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `(2u8..=std::u8::MAX, _)` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:13:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:6:11 | LL | match (0u8, 0u8) { | ^^^^^^^^^^ pattern `(2u8..=std::u8::MAX, _)` not covered @@ -7,7 +7,7 @@ LL | match (0u8, 0u8) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `((4u8..=std::u8::MAX))` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:17:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:10:11 | LL | match ((0u8,),) { | ^^^^^^^^^ pattern `((4u8..=std::u8::MAX))` not covered @@ -15,19 +15,13 @@ LL | match ((0u8,),) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `(Some(2u8..=std::u8::MAX))` not covered - --> $DIR/exhaustiveness-non-exhaustive.rs:21:11 + --> $DIR/exhaustiveness-non-exhaustive.rs:14:11 | LL | match (Some(0u8),) { | ^^^^^^^^^^^^ pattern `(Some(2u8..=std::u8::MAX))` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: or-patterns are not fully implemented yet - --> $DIR/exhaustiveness-non-exhaustive.rs:9:10 - | -LL | (0 | _,) => {} - | ^^^^^ - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index 9b62810d29db9..8dcf8792f6f96 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -1,13 +1,10 @@ #![feature(or_patterns)] #![deny(unreachable_patterns)] -// We wrap patterns in a tuple because top-level or-patterns are special-cased for now. -fn main() { - // Get the fatal error out of the way - match (0,) { - (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet - } +// check-pass +// We wrap patterns in a tuple because top-level or-patterns were special-cased. +fn main() { match (0,) { (1 | 2,) => {} _ => {} diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.stderr b/src/test/ui/or-patterns/exhaustiveness-pass.stderr deleted file mode 100644 index dc5a4186ac700..0000000000000 --- a/src/test/ui/or-patterns/exhaustiveness-pass.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: or-patterns are not fully implemented yet - --> $DIR/exhaustiveness-pass.rs:9:10 - | -LL | (0 | _,) => {} - | ^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index dd1c16f500028..44bae282d8857 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -1,13 +1,8 @@ #![feature(or_patterns)] #![deny(unreachable_patterns)] -// We wrap patterns in a tuple because top-level or-patterns are special-cased for now. +// We wrap patterns in a tuple because top-level or-patterns were special-cased. fn main() { - // Get the fatal error out of the way - match (0u8,) { - (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet - } - match (0u8,) { (1 | 2,) => {} (1,) => {} //~ ERROR unreachable pattern diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index 1f07c27afad9c..bef6f8270bc54 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -1,110 +1,104 @@ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:15:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:8:9 | LL | (1,) => {} | ^^^^ | note: the lint level is defined here - --> $DIR/exhaustiveness-unreachable-pattern.rs:3:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:2:9 | LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:20:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:13:9 | LL | (2,) => {} | ^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:26:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:19:9 | LL | (1 | 2,) => {} | ^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:31:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:24:9 | LL | (1, 3) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:32:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:25:9 | LL | (1, 4) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:33:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:26:9 | LL | (2, 4) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:34:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:27:9 | LL | (2 | 1, 4) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:36:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:29:9 | LL | (1, 4 | 5) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:41:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:34:9 | LL | (Some(1),) => {} | ^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:42:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:35:9 | LL | (None,) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:47:9 + --> $DIR/exhaustiveness-unreachable-pattern.rs:40:9 | -LL | ((1..=4,),) => {}, +LL | ((1..=4,),) => {} | ^^^^^^^^^^^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:53:12 + --> $DIR/exhaustiveness-unreachable-pattern.rs:45:14 | -LL | | 1,) => {} - | ^ +LL | (1 | 1,) => {} + | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:60:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:52:15 | LL | | 0] => {} | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:58:15 + --> $DIR/exhaustiveness-unreachable-pattern.rs:50:15 | LL | | 0 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:68:10 + --> $DIR/exhaustiveness-unreachable-pattern.rs:60:10 | LL | [1 | ^ error: unreachable pattern - --> $DIR/exhaustiveness-unreachable-pattern.rs:74:14 + --> $DIR/exhaustiveness-unreachable-pattern.rs:66:14 | LL | Some(0 | ^ -error: or-patterns are not fully implemented yet - --> $DIR/exhaustiveness-unreachable-pattern.rs:9:10 - | -LL | (0 | _,) => {} - | ^^^^^ - -error: aborting due to 17 previous errors +error: aborting due to 16 previous errors diff --git a/src/test/ui/or-patterns/feature-gate-const-fn.rs b/src/test/ui/or-patterns/feature-gate-const-fn.rs index d21cf3dc72c99..2ef5537db60ad 100644 --- a/src/test/ui/or-patterns/feature-gate-const-fn.rs +++ b/src/test/ui/or-patterns/feature-gate-const-fn.rs @@ -30,6 +30,8 @@ fn main() { let x = Ok(3); let Ok(y) | Err(y) = x; //~^ ERROR or-pattern is not allowed in a `const` + //~| ERROR constant contains unimplemented expression type + //~| ERROR constant contains unimplemented expression type 2 }]; } diff --git a/src/test/ui/or-patterns/feature-gate-const-fn.stderr b/src/test/ui/or-patterns/feature-gate-const-fn.stderr index 112bc62517260..9284e2d442dfa 100644 --- a/src/test/ui/or-patterns/feature-gate-const-fn.stderr +++ b/src/test/ui/or-patterns/feature-gate-const-fn.stderr @@ -52,6 +52,19 @@ LL | let Ok(y) | Err(y) = x; = note: for more information, see https://github.com/rust-lang/rust/issues/49146 = help: add `#![feature(const_if_match)]` to the crate attributes to enable -error: aborting due to 6 previous errors +error[E0019]: constant contains unimplemented expression type + --> $DIR/feature-gate-const-fn.rs:31:25 + | +LL | let Ok(y) | Err(y) = x; + | ^ + +error[E0019]: constant contains unimplemented expression type + --> $DIR/feature-gate-const-fn.rs:31:16 + | +LL | let Ok(y) | Err(y) = x; + | ^ + +error: aborting due to 8 previous errors -For more information about this error, try `rustc --explain E0658`. +Some errors have detailed explanations: E0019, E0658. +For more information about an error, try `rustc --explain E0019`. diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs b/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs index f6b317886bfad..4489303638358 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.rs @@ -6,7 +6,6 @@ fn foo(res: Result) -> u32 { let Ok(x) = res; //~^ ERROR refutable pattern x - //~^ ERROR use of possibly-uninitialized variable: `x` } fn main() { diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr index f9ae75b18317d..aa23aed4b425a 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -11,13 +11,6 @@ help: you might want to use `if let` to ignore the variant that isn't matched LL | if let Ok(x) = res { /* */ } | -error[E0381]: use of possibly-uninitialized variable: `x` - --> $DIR/recursive-types-are-not-uninhabited.rs:8:5 - | -LL | x - | ^ use of possibly-uninitialized `x` - -error: aborting due to 2 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0005, E0381. -For more information about an error, try `rustc --explain E0005`. +For more information about this error, try `rustc --explain E0005`. diff --git a/src/test/ui/suggestions/dont-suggest-ref/duplicate-suggestions.stderr b/src/test/ui/suggestions/dont-suggest-ref/duplicate-suggestions.stderr index 1f1211aa198f8..612fae208cc9d 100644 --- a/src/test/ui/suggestions/dont-suggest-ref/duplicate-suggestions.stderr +++ b/src/test/ui/suggestions/dont-suggest-ref/duplicate-suggestions.stderr @@ -65,13 +65,18 @@ LL | match &(e.clone(), e.clone()) { | ^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | &(Either::One(_t), Either::Two(_u)) - | ----------------------------------- - | | | | - | | | ...and here - | | data moved here - | help: consider removing the `&`: `(Either::One(_t), Either::Two(_u))` + | -- -- ...and here + | | + | data moved here | = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider removing the `&` + | +LL | (Either::One(_t), Either::Two(_u)) +LL | +LL | +LL | | &(Either::Two(_t), Either::One(_u)) => (), + | error[E0507]: cannot move out of a shared reference --> $DIR/duplicate-suggestions.rs:70:11 @@ -170,13 +175,18 @@ LL | match &mut (em.clone(), em.clone()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | &mut (Either::One(_t), Either::Two(_u)) - | --------------------------------------- - | | | | - | | | ...and here - | | data moved here - | help: consider removing the `&mut`: `(Either::One(_t), Either::Two(_u))` + | -- -- ...and here + | | + | data moved here | = note: move occurs because these variables have types that don't implement the `Copy` trait +help: consider removing the `&mut` + | +LL | (Either::One(_t), Either::Two(_u)) +LL | +LL | +LL | | &mut (Either::Two(_t), Either::One(_u)) => (), + | error[E0507]: cannot move out of a mutable reference --> $DIR/duplicate-suggestions.rs:122:11 diff --git a/src/test/ui/suggestions/dont-suggest-ref/simple.stderr b/src/test/ui/suggestions/dont-suggest-ref/simple.stderr index ac91ac43736f9..5550e097cf554 100644 --- a/src/test/ui/suggestions/dont-suggest-ref/simple.stderr +++ b/src/test/ui/suggestions/dont-suggest-ref/simple.stderr @@ -263,11 +263,18 @@ LL | match r { | ^ LL | LL | &Either::One(_t) - | ---------------- - | | | - | | data moved here - | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait - | help: consider removing the `&`: `Either::One(_t)` + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: consider removing the `&` + | +LL | Either::One(_t) +LL | +LL | +LL | | &Either::Two(_t) => (), + | error[E0507]: cannot move out of `r.0` which is behind a shared reference --> $DIR/simple.rs:188:11 @@ -502,11 +509,18 @@ LL | match &e { | ^^ LL | LL | &Either::One(_t) - | ---------------- - | | | - | | data moved here - | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait - | help: consider removing the `&`: `Either::One(_t)` + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: consider removing the `&` + | +LL | Either::One(_t) +LL | +LL | +LL | | &Either::Two(_t) => (), + | error[E0507]: cannot move out of a shared reference --> $DIR/simple.rs:308:11 @@ -571,11 +585,18 @@ LL | match &mut em { | ^^^^^^^ LL | LL | &mut Either::One(_t) - | -------------------- - | | | - | | data moved here - | | move occurs because `_t` has type `X`, which does not implement the `Copy` trait - | help: consider removing the `&mut`: `Either::One(_t)` + | -- + | | + | data moved here + | move occurs because `_t` has type `X`, which does not implement the `Copy` trait + | +help: consider removing the `&mut` + | +LL | Either::One(_t) +LL | +LL | +LL | | &mut Either::Two(_t) => (), + | error[E0507]: cannot move out of a mutable reference --> $DIR/simple.rs:343:11 From 0b1ff27cd8968454771d419703873e3f98caf2eb Mon Sep 17 00:00:00 2001 From: Dan Robertson Date: Sun, 14 Jul 2019 01:33:02 +0000 Subject: [PATCH 07/13] 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 | 33 +++++++++++++ src/test/ui/or-patterns/basic-switchint.rs | 57 ++++++++++++++++++++++ src/test/ui/or-patterns/mix-with-wild.rs | 19 ++++++++ src/test/ui/or-patterns/struct-like.rs | 42 ++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 src/test/ui/or-patterns/basic-switch.rs create mode 100644 src/test/ui/or-patterns/basic-switchint.rs create mode 100644 src/test/ui/or-patterns/mix-with-wild.rs create mode 100644 src/test/ui/or-patterns/struct-like.rs 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..6daa9d9255b80 --- /dev/null +++ b/src/test/ui/or-patterns/basic-switch.rs @@ -0,0 +1,33 @@ +// Test basic or-patterns when the target pattern type will be lowered to a +// `Switch` (an `enum`). + +// run-pass + +#![feature(or_patterns)] + +#[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-switchint.rs b/src/test/ui/or-patterns/basic-switchint.rs new file mode 100644 index 0000000000000..2ae5f2681655a --- /dev/null +++ b/src/test/ui/or-patterns/basic-switchint.rs @@ -0,0 +1,57 @@ +// Test basic or-patterns when the target pattern type will be lowered to +// a `SwitchInt`. This will happen when the target type is an integer. + +// run-pass + +#![feature(or_patterns)] + +#[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/mix-with-wild.rs b/src/test/ui/or-patterns/mix-with-wild.rs new file mode 100644 index 0000000000000..37f20df1b312d --- /dev/null +++ b/src/test/ui/or-patterns/mix-with-wild.rs @@ -0,0 +1,19 @@ +// 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)] + +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/struct-like.rs b/src/test/ui/or-patterns/struct-like.rs new file mode 100644 index 0000000000000..3794a8b6c1510 --- /dev/null +++ b/src/test/ui/or-patterns/struct-like.rs @@ -0,0 +1,42 @@ +// run-pass + +#![feature(or_patterns)] + +#[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)); +} From c7e6f88926e746d310ca15ce9761b3fd434dbfd2 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Fri, 27 Dec 2019 18:11:20 +0000 Subject: [PATCH 08/13] Add more tests for or-patterns --- src/test/mir-opt/exponential-or.rs | 76 ++++++++++ src/test/ui/borrowck/or-patterns.rs | 64 ++++++++ src/test/ui/borrowck/or-patterns.stderr | 141 ++++++++++++++++++ src/test/ui/or-patterns/bindings-runpass-1.rs | 25 ++++ src/test/ui/or-patterns/bindings-runpass-2.rs | 32 ++++ src/test/ui/or-patterns/for-loop.rs | 18 +++ src/test/ui/or-patterns/if-let-while-let.rs | 22 +++ .../issue-67514-irrefutable-param.rs | 11 ++ src/test/ui/or-patterns/let-pattern.rs | 19 +++ .../ui/or-patterns/search-via-bindings.rs | 66 ++++++++ 10 files changed, 474 insertions(+) create mode 100644 src/test/mir-opt/exponential-or.rs create mode 100644 src/test/ui/borrowck/or-patterns.rs create mode 100644 src/test/ui/borrowck/or-patterns.stderr create mode 100644 src/test/ui/or-patterns/bindings-runpass-1.rs create mode 100644 src/test/ui/or-patterns/bindings-runpass-2.rs create mode 100644 src/test/ui/or-patterns/for-loop.rs create mode 100644 src/test/ui/or-patterns/if-let-while-let.rs create mode 100644 src/test/ui/or-patterns/issue-67514-irrefutable-param.rs create mode 100644 src/test/ui/or-patterns/let-pattern.rs create mode 100644 src/test/ui/or-patterns/search-via-bindings.rs diff --git a/src/test/mir-opt/exponential-or.rs b/src/test/mir-opt/exponential-or.rs new file mode 100644 index 0000000000000..4c23582e1f894 --- /dev/null +++ b/src/test/mir-opt/exponential-or.rs @@ -0,0 +1,76 @@ +// 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, u32)) -> u32 { + match x { + (y @ (1 | 4), true | false, Some(1 | 8) | None, z @ (6..=9 | 13..=16)) => y ^ 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)); +// switchInt(move _2) -> [0isize: bb4, 1isize: bb3, otherwise: bb1]; +// } +// bb3: { +// switchInt((((_1.2: std::option::Option) 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); +// _10 = _8; +// _0 = BitXor(move _9, move _10); +// StorageDead(_10); +// StorageDead(_9); +// StorageDead(_8); +// StorageDead(_7); +// goto -> bb10; +// } +// bb10: { +// return; +// } +// END rustc.match_tuple.SimplifyCfg-initial.after.mir diff --git a/src/test/ui/borrowck/or-patterns.rs b/src/test/ui/borrowck/or-patterns.rs new file mode 100644 index 0000000000000..5b31e2d76a05b --- /dev/null +++ b/src/test/ui/borrowck/or-patterns.rs @@ -0,0 +1,64 @@ +// Test that borrow check considers all choices in an or pattern, even the +// unreachable ones. + +#![feature(or_patterns)] + +fn or_pattern_moves_all(x: ((String, String),)) { + match x { + ((y, _) | (_, y),) => (), + } + &x.0 .0; + //~^ ERROR borrow of moved value + &x.0 .1; + //~^ ERROR borrow of moved value +} + +fn or_pattern_borrows_all(mut x: ((String, String),)) { + let r = match x { + ((ref y, _) | (_, ref y),) => y, + }; + &mut x.0 .0; + //~^ ERROR cannot borrow + &mut x.0 .1; + //~^ ERROR cannot borrow + drop(r); +} + +fn or_pattern_borrows_all_mut(mut x: ((String, String),)) { + let r = match x { + ((ref mut y, _) | (_, ref mut y),) => y, + }; + &x.0 .0; + //~^ ERROR cannot borrow + &x.0 .1; + //~^ ERROR cannot borrow + drop(r); +} + +fn let_or_pattern_moves_all(x: ((String, String),)) { + let ((y, _) | (_, y),) = x; + &x.0 .0; + //~^ ERROR borrow of moved value + &x.0 .1; + //~^ ERROR borrow of moved value +} + +fn let_or_pattern_borrows_all(mut x: ((String, String),)) { + let ((ref r, _) | (_, ref r),) = x; + &mut x.0 .0; + //~^ ERROR cannot borrow + &mut x.0 .1; + //~^ ERROR cannot borrow + drop(r); +} + +fn let_or_pattern_borrows_all_mut(mut x: ((String, String),)) { + let ((ref mut r, _) | (_, ref mut r),) = x; + &x.0 .0; + //~^ ERROR cannot borrow + &x.0 .1; + //~^ ERROR cannot borrow + drop(r); +} + +fn main() {} diff --git a/src/test/ui/borrowck/or-patterns.stderr b/src/test/ui/borrowck/or-patterns.stderr new file mode 100644 index 0000000000000..d3f3544426aad --- /dev/null +++ b/src/test/ui/borrowck/or-patterns.stderr @@ -0,0 +1,141 @@ +error[E0382]: borrow of moved value: `x.0.0` + --> $DIR/or-patterns.rs:10:5 + | +LL | ((y, _) | (_, y),) => (), + | - value moved here +LL | } +LL | &x.0 .0; + | ^^^^^^^ value borrowed here after move + | + = note: move occurs because `x.0.0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `x.0.1` + --> $DIR/or-patterns.rs:12:5 + | +LL | ((y, _) | (_, y),) => (), + | - value moved here +... +LL | &x.0 .1; + | ^^^^^^^ value borrowed here after move + | + = note: move occurs because `x.0.1` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0502]: cannot borrow `x.0.0` as mutable because it is also borrowed as immutable + --> $DIR/or-patterns.rs:20:5 + | +LL | ((ref y, _) | (_, ref y),) => y, + | ----- immutable borrow occurs here +LL | }; +LL | &mut x.0 .0; + | ^^^^^^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x.0.1` as mutable because it is also borrowed as immutable + --> $DIR/or-patterns.rs:22:5 + | +LL | ((ref y, _) | (_, ref y),) => y, + | ----- immutable borrow occurs here +... +LL | &mut x.0 .1; + | ^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x.0.0` as immutable because it is also borrowed as mutable + --> $DIR/or-patterns.rs:31:5 + | +LL | ((ref mut y, _) | (_, ref mut y),) => y, + | --------- mutable borrow occurs here +LL | }; +LL | &x.0 .0; + | ^^^^^^^ immutable borrow occurs here +... +LL | drop(r); + | - mutable borrow later used here + +error[E0502]: cannot borrow `x.0.1` as immutable because it is also borrowed as mutable + --> $DIR/or-patterns.rs:33:5 + | +LL | ((ref mut y, _) | (_, ref mut y),) => y, + | --------- mutable borrow occurs here +... +LL | &x.0 .1; + | ^^^^^^^ immutable borrow occurs here +LL | +LL | drop(r); + | - mutable borrow later used here + +error[E0382]: borrow of moved value: `x.0.0` + --> $DIR/or-patterns.rs:40:5 + | +LL | let ((y, _) | (_, y),) = x; + | - value moved here +LL | &x.0 .0; + | ^^^^^^^ value borrowed here after move + | + = note: move occurs because `x.0.0` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0382]: borrow of moved value: `x.0.1` + --> $DIR/or-patterns.rs:42:5 + | +LL | let ((y, _) | (_, y),) = x; + | - value moved here +... +LL | &x.0 .1; + | ^^^^^^^ value borrowed here after move + | + = note: move occurs because `x.0.1` has type `std::string::String`, which does not implement the `Copy` trait + +error[E0502]: cannot borrow `x.0.0` as mutable because it is also borrowed as immutable + --> $DIR/or-patterns.rs:48:5 + | +LL | let ((ref r, _) | (_, ref r),) = x; + | ----- immutable borrow occurs here +LL | &mut x.0 .0; + | ^^^^^^^^^^^ mutable borrow occurs here +... +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x.0.1` as mutable because it is also borrowed as immutable + --> $DIR/or-patterns.rs:50:5 + | +LL | let ((ref r, _) | (_, ref r),) = x; + | ----- immutable borrow occurs here +... +LL | &mut x.0 .1; + | ^^^^^^^^^^^ mutable borrow occurs here +LL | +LL | drop(r); + | - immutable borrow later used here + +error[E0502]: cannot borrow `x.0.0` as immutable because it is also borrowed as mutable + --> $DIR/or-patterns.rs:57:5 + | +LL | let ((ref mut r, _) | (_, ref mut r),) = x; + | --------- mutable borrow occurs here +LL | &x.0 .0; + | ^^^^^^^ immutable borrow occurs here +... +LL | drop(r); + | - mutable borrow later used here + +error[E0502]: cannot borrow `x.0.1` as immutable because it is also borrowed as mutable + --> $DIR/or-patterns.rs:59:5 + | +LL | let ((ref mut r, _) | (_, ref mut r),) = x; + | --------- mutable borrow occurs here +... +LL | &x.0 .1; + | ^^^^^^^ immutable borrow occurs here +LL | +LL | drop(r); + | - mutable borrow later used here + +error: aborting due to 12 previous errors + +Some errors have detailed explanations: E0382, E0502. +For more information about an error, try `rustc --explain E0382`. diff --git a/src/test/ui/or-patterns/bindings-runpass-1.rs b/src/test/ui/or-patterns/bindings-runpass-1.rs new file mode 100644 index 0000000000000..0087167af7ecc --- /dev/null +++ b/src/test/ui/or-patterns/bindings-runpass-1.rs @@ -0,0 +1,25 @@ +// run-pass + +#![feature(or_patterns)] + +fn two_bindings(x: &((bool, bool), u8)) -> u8 { + match x { + &((true, y) | (y, true), z @ (0 | 4)) => (y as u8) + z, + _ => 20, + } +} + +fn main() { + assert_eq!(two_bindings(&((false, false), 0)), 20); + assert_eq!(two_bindings(&((false, true), 0)), 0); + assert_eq!(two_bindings(&((true, false), 0)), 0); + assert_eq!(two_bindings(&((true, true), 0)), 1); + assert_eq!(two_bindings(&((false, false), 4)), 20); + assert_eq!(two_bindings(&((false, true), 4)), 4); + assert_eq!(two_bindings(&((true, false), 4)), 4); + assert_eq!(two_bindings(&((true, true), 4)), 5); + assert_eq!(two_bindings(&((false, false), 3)), 20); + assert_eq!(two_bindings(&((false, true), 3)), 20); + assert_eq!(two_bindings(&((true, false), 3)), 20); + assert_eq!(two_bindings(&((true, true), 3)), 20); +} diff --git a/src/test/ui/or-patterns/bindings-runpass-2.rs b/src/test/ui/or-patterns/bindings-runpass-2.rs new file mode 100644 index 0000000000000..0e1eb7b2e030b --- /dev/null +++ b/src/test/ui/or-patterns/bindings-runpass-2.rs @@ -0,0 +1,32 @@ +// run-pass + +#![feature(or_patterns)] + +fn or_at(x: Result) -> u32 { + match x { + Ok(x @ 4) | Err(x @ (6 | 8)) => x, + Ok(x @ 1 | x @ 2) => x, + Err(x @ (0..=10 | 30..=40)) if x % 2 == 0 => x + 100, + Err(x @ 0..=40) => x + 200, + _ => 500, + } +} + +fn main() { + assert_eq!(or_at(Ok(1)), 1); + assert_eq!(or_at(Ok(2)), 2); + assert_eq!(or_at(Ok(3)), 500); + assert_eq!(or_at(Ok(4)), 4); + assert_eq!(or_at(Ok(5)), 500); + assert_eq!(or_at(Ok(6)), 500); + assert_eq!(or_at(Err(1)), 201); + assert_eq!(or_at(Err(2)), 102); + assert_eq!(or_at(Err(3)), 203); + assert_eq!(or_at(Err(4)), 104); + assert_eq!(or_at(Err(5)), 205); + assert_eq!(or_at(Err(6)), 6); + assert_eq!(or_at(Err(7)), 207); + assert_eq!(or_at(Err(8)), 8); + assert_eq!(or_at(Err(20)), 220); + assert_eq!(or_at(Err(50)), 500); +} diff --git a/src/test/ui/or-patterns/for-loop.rs b/src/test/ui/or-patterns/for-loop.rs new file mode 100644 index 0000000000000..b79af6c402ec5 --- /dev/null +++ b/src/test/ui/or-patterns/for-loop.rs @@ -0,0 +1,18 @@ +// Check that or patterns are lowered correctly in `for` loops. +// run-pass + +#![feature(or_patterns)] + +fn main() { + let v = vec![Ok(2), Err(3), Ok(5)]; + let mut w = Vec::new(); + for &(Ok(i) | Err(i)) in &v { + w.push(i); + } + let mut u = Vec::new(); + for Ok(i) | Err(i) in v { + u.push(i); + } + assert_eq!(w, [2, 3, 5]); + assert_eq!(u, [2, 3, 5]); +} diff --git a/src/test/ui/or-patterns/if-let-while-let.rs b/src/test/ui/or-patterns/if-let-while-let.rs new file mode 100644 index 0000000000000..9256360b29db9 --- /dev/null +++ b/src/test/ui/or-patterns/if-let-while-let.rs @@ -0,0 +1,22 @@ +// Check that or patterns are lowered correctly in `if let` and `while let` expressions. +// run-pass + +#![feature(or_patterns)] + +fn main() { + let mut opt = Some(3); + let mut w = Vec::new(); + while let Some(ref mut val @ (3 | 4 | 6)) = opt { + w.push(*val); + *val += 1; + } + assert_eq!(w, [3, 4]); + if let &(None | Some(6 | 7)) = &opt { + unreachable!(); + } + if let Some(x @ (4 | 5 | 6)) = opt { + assert_eq!(x, 5); + } else { + unreachable!(); + } +} diff --git a/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs b/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs new file mode 100644 index 0000000000000..0c2ae44e546d9 --- /dev/null +++ b/src/test/ui/or-patterns/issue-67514-irrefutable-param.rs @@ -0,0 +1,11 @@ +// Check that we don't ICE for irrefutable or-patterns in function parameters + +// check-pass + +#![feature(or_patterns)] + +fn foo((Some(_) | None): Option) {} + +fn main() { + foo(None); +} diff --git a/src/test/ui/or-patterns/let-pattern.rs b/src/test/ui/or-patterns/let-pattern.rs new file mode 100644 index 0000000000000..07e37412ce842 --- /dev/null +++ b/src/test/ui/or-patterns/let-pattern.rs @@ -0,0 +1,19 @@ +#![feature(or_patterns)] + +// run-pass + +fn or_pat_let(x: Result) -> u32 { + let Ok(y) | Err(y) = x; + y +} + +fn or_pat_arg((Ok(y) | Err(y)): Result) -> u32 { + y +} + +fn main() { + assert_eq!(or_pat_let(Ok(3)), 3); + assert_eq!(or_pat_let(Err(5)), 5); + assert_eq!(or_pat_arg(Ok(7)), 7); + assert_eq!(or_pat_arg(Err(9)), 9); +} diff --git a/src/test/ui/or-patterns/search-via-bindings.rs b/src/test/ui/or-patterns/search-via-bindings.rs new file mode 100644 index 0000000000000..eb127b881cd37 --- /dev/null +++ b/src/test/ui/or-patterns/search-via-bindings.rs @@ -0,0 +1,66 @@ +// Check that we expand multiple or-patterns from left to right. + +// run-pass + +#![feature(or_patterns)] +#![allow(unreachable_patterns)] // FIXME(or-patterns) this shouldn't trigger + +fn search(target: (bool, bool, bool)) -> u32 { + let x = ((false, true), (false, true), (false, true)); + let mut guard_count = 0; + match x { + ((a, _) | (_, a), (b @ _, _) | (_, b @ _), (c @ false, _) | (_, c @ true)) + if { + guard_count += 1; + (a, b, c) == target + } => + { + guard_count + } + _ => unreachable!(), + } +} + +// Equivalent to the above code, but hopefully easier to understand. +fn search_old_style(target: (bool, bool, bool)) -> u32 { + let x = ((false, true), (false, true), (false, true)); + let mut guard_count = 0; + match x { + ((a, _), (b @ _, _), (c @ false, _)) + | ((a, _), (b @ _, _), (_, c @ true)) + | ((a, _), (_, b @ _), (c @ false, _)) + | ((a, _), (_, b @ _), (_, c @ true)) + | ((_, a), (b @ _, _), (c @ false, _)) + | ((_, a), (b @ _, _), (_, c @ true)) + | ((_, a), (_, b @ _), (c @ false, _)) + | ((_, a), (_, b @ _), (_, c @ true)) + if { + guard_count += 1; + (a, b, c) == target + } => + { + guard_count + } + _ => unreachable!(), + } +} + +fn main() { + assert_eq!(search((false, false, false)), 1); + assert_eq!(search((false, false, true)), 2); + assert_eq!(search((false, true, false)), 3); + assert_eq!(search((false, true, true)), 4); + assert_eq!(search((true, false, false)), 5); + assert_eq!(search((true, false, true)), 6); + assert_eq!(search((true, true, false)), 7); + assert_eq!(search((true, true, true)), 8); + + assert_eq!(search_old_style((false, false, false)), 1); + assert_eq!(search_old_style((false, false, true)), 2); + assert_eq!(search_old_style((false, true, false)), 3); + assert_eq!(search_old_style((false, true, true)), 4); + assert_eq!(search_old_style((true, false, false)), 5); + assert_eq!(search_old_style((true, false, true)), 6); + assert_eq!(search_old_style((true, true, false)), 7); + assert_eq!(search_old_style((true, true, true)), 8); +} From 5f90dbd5e86a5ba68ec03cef5dd2c2418f587dbb Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 29 Dec 2019 17:00:23 +0000 Subject: [PATCH 09/13] Make `Candidate` private --- src/librustc_mir_build/build/matches/mod.rs | 2 +- src/librustc_mir_build/build/matches/simplify.rs | 5 ++++- src/librustc_mir_build/build/matches/test.rs | 10 +++++----- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index 928363246c2c0..7e7035e89abee 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -659,7 +659,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } #[derive(Debug)] -crate struct Candidate<'pat, 'tcx> { +struct Candidate<'pat, 'tcx> { /// `Span` of the original pattern that gave rise to this candidate span: Span, diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index 3a806ab6bff0a..d3ed7c4daf7eb 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -38,7 +38,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// only generates a single switch. If this happens this method returns /// `true`. - crate fn simplify_candidate<'pat>(&mut self, candidate: &mut Candidate<'pat, 'tcx>) -> bool { + pub(super) 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); diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs index ff95f16225751..dd0102e1c410f 100644 --- a/src/librustc_mir_build/build/matches/test.rs +++ b/src/librustc_mir_build/build/matches/test.rs @@ -24,7 +24,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Identifies what test is needed to decide if `match_pair` is applicable. /// /// It is a bug to call this with a simplifiable pattern. - crate fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { + pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> { match *match_pair.pattern.kind { PatKind::Variant { ref adt_def, substs: _, variant_index: _, subpatterns: _ } => Test { span: match_pair.pattern.span, @@ -81,7 +81,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - crate fn add_cases_to_switch<'pat>( + pub(super) fn add_cases_to_switch<'pat>( &mut self, test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, @@ -125,7 +125,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - crate fn add_variants_to_switch<'pat>( + pub(super) fn add_variants_to_switch<'pat>( &mut self, test_place: &Place<'tcx>, candidate: &Candidate<'pat, 'tcx>, @@ -152,7 +152,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - crate fn perform_test( + pub(super) fn perform_test( &mut self, block: BasicBlock, place: &Place<'tcx>, @@ -498,7 +498,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// that it *doesn't* apply. For now, we return false, indicate that the /// test does not apply to this candidate, but it might be we can get /// tighter match code if we do something a bit different. - crate fn sort_candidate<'pat>( + pub(super) fn sort_candidate<'pat>( &mut self, test_place: &Place<'tcx>, test: &Test<'tcx>, From 89e52e2ca9e5bf63c197c2dfa7d062f409fbeec7 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 29 Dec 2019 17:28:41 +0000 Subject: [PATCH 10/13] Address review comments --- src/librustc_mir_build/build/matches/mod.rs | 221 +++++++++++------- .../build/matches/simplify.rs | 18 +- 2 files changed, 143 insertions(+), 96 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index 7e7035e89abee..d5ac2a14129ce 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -153,18 +153,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { arms.iter() .map(|arm| { let arm_has_guard = arm.guard.is_some(); - let arm_candidate = Candidate { - span: arm.pattern.span, - match_pairs: smallvec![MatchPair::new(*scrutinee, &arm.pattern),], - bindings: vec![], - ascriptions: vec![], - has_guard: arm_has_guard, - needs_otherwise_block: arm_has_guard, - otherwise_block: None, - pre_binding_block: None, - next_candidate_pre_binding_block: None, - subcandidates: vec![], - }; + let arm_candidate = Candidate::new(*scrutinee, &arm.pattern, arm_has_guard); (arm, arm_candidate) }) .collect() @@ -195,10 +184,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.match_candidates(scrutinee_span, block, &mut otherwise, candidates, &mut fake_borrows); if let Some(otherwise_block) = otherwise { + // See the doc comment on `match_candidates` for why we may have an + // otherwise block. Match checking will ensure this is actually + // unreachable. let source_info = self.source_info(scrutinee_span); self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable); } + // Link each leaf candidate to the `pre_binding_block` of the next one. let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None; for candidate in candidates { @@ -449,29 +442,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { initializer: &Place<'tcx>, set_match_place: bool, ) -> BlockAnd<()> { - // create a dummy candidate - let mut candidate = Candidate { - span: irrefutable_pat.span, - has_guard: false, - needs_otherwise_block: false, - match_pairs: smallvec![MatchPair::new(*initializer, &irrefutable_pat)], - bindings: vec![], - ascriptions: vec![], - - // since we don't call `match_candidates`, next fields are unused - otherwise_block: None, - pre_binding_block: None, - next_candidate_pre_binding_block: None, - subcandidates: vec![], - }; + let mut candidate = Candidate::new(*initializer, &irrefutable_pat, false); let fake_borrow_temps = self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]); - // for matches and function arguments, the place that is being matched + // For matches and function arguments, the place that is being matched // can be set when creating the variables. But the place for // let PATTERN = ... might not even exist until we do the assignment. - // so we set it here instead + // so we set it here instead. if set_match_place { let mut candidate_ref = &candidate; while let Some(next) = { @@ -487,6 +466,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { bug!("Let binding to non-user variable.") } } + // All of the subcandidates should bind the same locals, so we + // only visit the first one. candidate_ref.subcandidates.get(0) } { candidate_ref = next; @@ -666,10 +647,6 @@ struct Candidate<'pat, 'tcx> { /// This `Candidate` has a guard. has_guard: bool, - /// This `Candidate` needs and otherwise block, either because it has a - /// guard or it has subcandidates. - needs_otherwise_block: bool, - /// All of these must be satisfied... match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>, @@ -690,7 +667,21 @@ struct Candidate<'pat, 'tcx> { next_candidate_pre_binding_block: Option, } -impl Candidate<'_, '_> { +impl<'tcx, 'pat> Candidate<'pat, 'tcx> { + fn new(place: Place<'tcx>, pattern: &'pat Pat<'tcx>, has_guard: bool) -> Self { + Candidate { + span: pattern.span, + has_guard, + match_pairs: smallvec![MatchPair { place, pattern }], + bindings: Vec::new(), + ascriptions: Vec::new(), + subcandidates: Vec::new(), + otherwise_block: None, + pre_binding_block: None, + next_candidate_pre_binding_block: None, + } + } + /// Visit the leaf candidates (those with no subcandidates) contained in /// this candidate. fn visit_leaves<'a>(&'a mut self, mut visit_leaf: impl FnMut(&'a mut Self)) { @@ -839,6 +830,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// /// If `fake_borrows` is Some, then places which need fake borrows /// will be added to it. + /// + /// For an example of a case where we set `otherwise_block`, even for an + /// exhaustive match consider: + /// + /// match x { + /// (true, true) => (), + /// (_, false) => (), + /// (false, true) => (), + /// } + /// + /// For this match we check if `x.0` matches `true` (for the first + /// arm) if that's false we check `x.1`, if it's `true` we check if + /// `x.0` matches `false` (for the third arm). In the (impossible at + /// runtime) case when `x.0` is now `true` we branch to + /// `otherwise_block`. fn match_candidates<'pat>( &mut self, span: Span, @@ -1009,7 +1015,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let fully_matched_with_guard = matched_candidates .iter() - .position(|c| !c.needs_otherwise_block) + .position(|c| !c.has_guard) .unwrap_or(matched_candidates.len() - 1); let (reachable_candidates, unreachable_candidates) = @@ -1021,7 +1027,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { assert!(candidate.otherwise_block.is_none()); assert!(candidate.pre_binding_block.is_none()); candidate.pre_binding_block = Some(next_prebinding); - if candidate.needs_otherwise_block { + if candidate.has_guard { + // Create the otherwise block for this candidate, which is the + // pre-binding block for the next candidate. next_prebinding = self.cfg.start_new_block(); candidate.otherwise_block = Some(next_prebinding); } @@ -1039,6 +1047,53 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { reachable_candidates.last_mut().unwrap().otherwise_block } + /// Tests a candidate where there are only or-patterns left to test, or + /// forwards to [Builder::test_candidates]. + /// + /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like + /// so + /// + /// ```text + /// [ start ] + /// | + /// [ match P, Q ] + /// | + /// +----------------------------------------+------------------------------------+ + /// | | | + /// [ P matches ] [ Q matches ] [ otherwise ] + /// | | | + /// [ match R, S ] [ match R, S ] | + /// | | | + /// +--------------+------------+ +--------------+------------+ | + /// | | | | | | | + /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] | + /// | | | | | | | + /// +--------------+------------|------------+--------------+ | | + /// | | | | + /// | +----------------------------------------+--------+ + /// | | + /// [ Success ] [ Failure ] + /// ``` + /// + /// In practice there are some complications: + /// + /// * If there's a guard, then the otherwise branch of the first match on + /// `R | S` goes to a test for whether `Q` matches. + /// * If neither `P` or `Q` has any bindings or type ascriptions and there + /// isn't a match guard, then we create a smaller CFG like: + /// + /// ```text + /// ... + /// +---------------+------------+ + /// | | | + /// [ P matches ] [ Q matches ] [ otherwise ] + /// | | | + /// +---------------+ | + /// | ... + /// [ match R, S ] + /// | + /// ... + /// ``` fn test_candidates_with_or( &mut self, span: Span, @@ -1049,42 +1104,53 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); - if let PatKind::Or { .. } = *first_candidate.match_pairs[0].pattern.kind { - let match_pairs = mem::take(&mut first_candidate.match_pairs); - first_candidate.needs_otherwise_block = true; - first_candidate.pre_binding_block = Some(block); + match *first_candidate.match_pairs[0].pattern.kind { + PatKind::Or { .. } => (), + _ => { + self.test_candidates(span, candidates, block, otherwise_block, fake_borrows); + return; + } + } - // We sort or-patterns to the end in `simplify_candidate`, so all - // the remaining match pairs are or-patterns. - for match_pair in match_pairs { - if let PatKind::Or { ref pats } = *match_pair.pattern.kind { - let or_span = match_pair.pattern.span; - let place = &match_pair.place; + let match_pairs = mem::take(&mut first_candidate.match_pairs); + first_candidate.pre_binding_block = Some(block); - first_candidate.visit_leaves(|leaf_candidate| { - self.test_or_pattern(leaf_candidate, pats, or_span, place, fake_borrows); - }); - } else { - bug!("Or patterns should have been sorted to the end"); - } + let mut otherwise = None; + for match_pair in match_pairs { + if let PatKind::Or { ref pats } = *match_pair.pattern.kind { + let or_span = match_pair.pattern.span; + let place = &match_pair.place; + + first_candidate.visit_leaves(|leaf_candidate| { + self.test_or_pattern( + leaf_candidate, + &mut otherwise, + pats, + or_span, + place, + fake_borrows, + ); + }); + } else { + bug!("Or-patterns should have been sorted to the end"); } - let remainder_start = - first_candidate.otherwise_block.unwrap_or_else(|| self.cfg.start_new_block()); - self.match_candidates( - span, - remainder_start, - otherwise_block, - remaining_candidates, - fake_borrows, - ) - } else { - self.test_candidates(span, candidates, block, otherwise_block, fake_borrows) } + + let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block()); + + self.match_candidates( + span, + remainder_start, + otherwise_block, + remaining_candidates, + fake_borrows, + ) } fn test_or_pattern<'pat>( &mut self, candidate: &mut Candidate<'pat, 'tcx>, + otherwise: &mut Option, pats: &'pat [Pat<'tcx>], or_span: Span, place: &Place<'tcx>, @@ -1093,27 +1159,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); let mut or_candidates: Vec<_> = pats .iter() - .map(|pat| { - let new_match_pair = smallvec![MatchPair { pattern: pat, place: place.clone() }]; - Candidate { - span: pat.span, - has_guard: candidate.has_guard, - needs_otherwise_block: candidate.needs_otherwise_block, - match_pairs: new_match_pair, - bindings: Vec::new(), - ascriptions: Vec::new(), - otherwise_block: None, - pre_binding_block: None, - next_candidate_pre_binding_block: None, - subcandidates: Vec::new(), - } - }) + .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard)) .collect(); let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); + let otherwise = if candidate.otherwise_block.is_some() { + &mut candidate.otherwise_block + } else { + otherwise + }; self.match_candidates( or_span, candidate.pre_binding_block.unwrap(), - &mut candidate.otherwise_block, + otherwise, &mut or_candidate_refs, fake_borrows, ); @@ -1128,10 +1185,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { candidate: &mut Candidate<'_, 'tcx>, source_info: SourceInfo, ) { - if candidate.subcandidates.is_empty() { + if candidate.subcandidates.is_empty() || candidate.has_guard { + // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard. return; } - let mut can_merge = !candidate.has_guard; + + let mut can_merge = true; // Not `Iterator::all` because we don't want to short-circuit. for subcandidate in &mut candidate.subcandidates { diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index d3ed7c4daf7eb..422a75a4f37a1 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -22,7 +22,6 @@ use rustc::ty::layout::{Integer, IntegerExt, Size}; use rustc_attr::{SignedInt, UnsignedInt}; use rustc_hir::RangeEnd; -use smallvec::smallvec; use std::mem; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -49,7 +48,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, ref place }] = *match_pairs { - candidate.subcandidates = self.create_or_subcanidates(candidate, place, pats); + candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); return true; } @@ -76,7 +75,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - fn create_or_subcanidates<'pat>( + fn create_or_subcandidates<'pat>( &mut self, candidate: &Candidate<'pat, 'tcx>, place: &Place<'tcx>, @@ -84,18 +83,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) -> Vec> { 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, - }; + let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard); self.simplify_candidate(&mut candidate); candidate }) From 1d90ed6370df81cb6e30816cb655c5a90af824a6 Mon Sep 17 00:00:00 2001 From: matthewjasper <20113453+matthewjasper@users.noreply.github.com> Date: Mon, 30 Dec 2019 17:47:10 +0000 Subject: [PATCH 11/13] Apply suggestions from code review Co-Authored-By: Mazdak Farrokhzad --- src/librustc_mir_build/build/matches/mod.rs | 8 +++++--- src/test/ui/or-patterns/basic-switchint.rs | 3 --- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index d5ac2a14129ce..ed5d74f2e6859 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -840,10 +840,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// (false, true) => (), /// } /// - /// For this match we check if `x.0` matches `true` (for the first - /// arm) if that's false we check `x.1`, if it's `true` we check if + /// For this match, we check if `x.0` matches `true` (for the first + /// arm). If that's false, we check `x.1`. If it's `true` we check if /// `x.0` matches `false` (for the third arm). In the (impossible at - /// runtime) case when `x.0` is now `true` we branch to + /// runtime) case when `x.0` is now `true`, we branch to /// `otherwise_block`. fn match_candidates<'pat>( &mut self, @@ -1104,6 +1104,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ) { let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap(); + // All of the or-patterns have been sorted to the end, so if the first + // pattern is an or-pattern we only have or-patterns. match *first_candidate.match_pairs[0].pattern.kind { PatKind::Or { .. } => (), _ => { diff --git a/src/test/ui/or-patterns/basic-switchint.rs b/src/test/ui/or-patterns/basic-switchint.rs index 2ae5f2681655a..c5a6d894eacec 100644 --- a/src/test/ui/or-patterns/basic-switchint.rs +++ b/src/test/ui/or-patterns/basic-switchint.rs @@ -26,9 +26,6 @@ fn test_foo(x: Foo) -> MatchArm { // 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), From 9b9dafb2c8795a542e1c93036afa02889ef696f0 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 11 Jan 2020 20:30:42 +0000 Subject: [PATCH 12/13] Make use of `Place: Copy` --- src/librustc_mir_build/build/matches/mod.rs | 42 +++++++++---------- .../build/matches/simplify.rs | 6 +-- src/librustc_mir_build/build/matches/test.rs | 26 +++++------- src/librustc_mir_build/build/mod.rs | 2 +- 4 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index ed5d74f2e6859..7ab9129398975 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -93,7 +93,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let scrutinee_place = unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,)); - let mut arm_candidates = self.create_match_candidates(&scrutinee_place, &arms); + let mut arm_candidates = self.create_match_candidates(scrutinee_place, &arms); let match_has_guard = arms.iter().any(|arm| arm.guard.is_some()); let mut candidates = @@ -103,7 +103,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.lower_match_tree(block, scrutinee_span, match_has_guard, &mut candidates); self.lower_match_arms( - &destination, + destination, scrutinee_place, scrutinee_span, arm_candidates, @@ -137,7 +137,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // check safety. let cause_matched_place = FakeReadCause::ForMatchedPlace; let source_info = self.source_info(scrutinee_span); - self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place.clone()); + self.cfg.push_fake_read(block, source_info, cause_matched_place, scrutinee_place); block.and(scrutinee_place) } @@ -145,7 +145,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// Create the initial `Candidate`s for a `match` expression. fn create_match_candidates<'pat>( &mut self, - scrutinee: &Place<'tcx>, + scrutinee: Place<'tcx>, arms: &'pat [Arm<'tcx>], ) -> Vec<(&'pat Arm<'tcx>, Candidate<'pat, 'tcx>)> { // Assemble a list of candidates: there is one candidate per pattern, @@ -153,7 +153,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { arms.iter() .map(|arm| { let arm_has_guard = arm.guard.is_some(); - let arm_candidate = Candidate::new(*scrutinee, &arm.pattern, arm_has_guard); + let arm_candidate = Candidate::new(scrutinee, &arm.pattern, arm_has_guard); (arm, arm_candidate) }) .collect() @@ -391,7 +391,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Inject a fake read, see comments on `FakeReadCause::ForLet`. let pattern_source_info = self.source_info(irrefutable_pat.span); let cause_let = FakeReadCause::ForLet; - self.cfg.push_fake_read(block, pattern_source_info, cause_let, place.clone()); + self.cfg.push_fake_read(block, pattern_source_info, cause_let, place); let ty_source_info = self.source_info(user_ty_span); let user_ty = pat_ascription_ty.user_ty( @@ -430,7 +430,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => { let place = unpack!(block = self.as_place(block, initializer)); - self.place_into_pattern(block, irrefutable_pat, &place, true) + self.place_into_pattern(block, irrefutable_pat, place, true) } } } @@ -439,10 +439,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut self, block: BasicBlock, irrefutable_pat: Pat<'tcx>, - initializer: &Place<'tcx>, + initializer: Place<'tcx>, set_match_place: bool, ) -> BlockAnd<()> { - let mut candidate = Candidate::new(*initializer, &irrefutable_pat, false); + let mut candidate = Candidate::new(initializer, &irrefutable_pat, false); let fake_borrow_temps = self.lower_match_tree(block, irrefutable_pat.span, false, &mut [&mut candidate]); @@ -461,7 +461,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. }, ))) = self.local_decls[local].local_info { - *match_place = Some(*initializer); + *match_place = Some(initializer); } else { bug!("Let binding to non-user variable.") } @@ -897,7 +897,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: Span, start_block: BasicBlock, otherwise_block: &mut Option, - candidates: &mut [&mut Candidate<_, 'tcx>], + candidates: &mut [&mut Candidate<'_, 'tcx>], fake_borrows: &mut Option>>, ) { // The candidates are sorted by priority. Check to see whether the @@ -1121,7 +1121,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { for match_pair in match_pairs { if let PatKind::Or { ref pats } = *match_pair.pattern.kind { let or_span = match_pair.pattern.span; - let place = &match_pair.place; + let place = match_pair.place; first_candidate.visit_leaves(|leaf_candidate| { self.test_or_pattern( @@ -1155,14 +1155,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { otherwise: &mut Option, pats: &'pat [Pat<'tcx>], or_span: Span, - place: &Place<'tcx>, + place: Place<'tcx>, fake_borrows: &mut Option>>, ) { debug!("test_or_pattern:\ncandidate={:#?}\npats={:#?}", candidate, pats); - let mut or_candidates: Vec<_> = pats - .iter() - .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard)) - .collect(); + let mut or_candidates: Vec<_> = + pats.iter().map(|pat| Candidate::new(place, pat, candidate.has_guard)).collect(); let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect(); let otherwise = if candidate.otherwise_block.is_some() { &mut candidate.otherwise_block @@ -1368,7 +1366,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } // Insert a Shallow borrow of any places that is switched on. - fake_borrows.as_mut().map(|fb| fb.insert(match_place.clone())); + fake_borrows.as_mut().map(|fb| fb.insert(match_place)); // perform the test, branching to one of N blocks. For each of // those N possible outcomes, create a (initially empty) @@ -1448,7 +1446,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target_blocks }; - self.perform_test(block, &match_place, &test, make_target_blocks); + self.perform_test(block, match_place, &test, make_target_blocks); } /// Determine the fake borrows that are needed from a set of places that @@ -1669,9 +1667,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let re_erased = tcx.lifetimes.re_erased; let scrutinee_source_info = self.source_info(scrutinee_span); - for (place, temp) in fake_borrows { - let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, *place); - self.cfg.push_assign(block, scrutinee_source_info, &Place::from(*temp), borrow); + for &(place, temp) in fake_borrows { + let borrow = Rvalue::Ref(re_erased, BorrowKind::Shallow, place); + self.cfg.push_assign(block, scrutinee_source_info, &Place::from(temp), borrow); } // the block to branch to if the guard fails; if there is no diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index 422a75a4f37a1..4213a30f2d8c1 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -45,7 +45,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { loop { let match_pairs = mem::take(&mut candidate.match_pairs); - if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, ref place }] = + if let [MatchPair { pattern: Pat { kind: box PatKind::Or { pats }, .. }, place }] = *match_pairs { candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats); @@ -78,12 +78,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn create_or_subcandidates<'pat>( &mut self, candidate: &Candidate<'pat, 'tcx>, - place: &Place<'tcx>, + place: Place<'tcx>, pats: &'pat [Pat<'tcx>], ) -> Vec> { pats.iter() .map(|pat| { - let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard); + let mut candidate = Candidate::new(place, pat, candidate.has_guard); self.simplify_candidate(&mut candidate); candidate }) diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs index dd0102e1c410f..1acfa7dddbe1f 100644 --- a/src/librustc_mir_build/build/matches/test.rs +++ b/src/librustc_mir_build/build/matches/test.rs @@ -155,7 +155,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { pub(super) fn perform_test( &mut self, block: BasicBlock, - place: &Place<'tcx>, + place: Place<'tcx>, test: &Test<'tcx>, make_target_blocks: impl FnOnce(&mut Self) -> Vec, ) { @@ -205,7 +205,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let discr_ty = adt_def.repr.discr_type().to_ty(tcx); let discr = self.temp(discr_ty, test.span); - self.cfg.push_assign(block, source_info, &discr, Rvalue::Discriminant(*place)); + self.cfg.push_assign(block, source_info, &discr, Rvalue::Discriminant(place)); assert_eq!(values.len() + 1, targets.len()); self.cfg.terminate( block, @@ -229,12 +229,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { 0 => (second_bb, first_bb), v => span_bug!(test.span, "expected boolean value but got {:?}", v), }; - TerminatorKind::if_( - self.hir.tcx(), - Operand::Copy(*place), - true_bb, - false_bb, - ) + TerminatorKind::if_(self.hir.tcx(), Operand::Copy(place), true_bb, false_bb) } else { bug!("`TestKind::SwitchInt` on `bool` should have two targets") } @@ -242,7 +237,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // The switch may be inexhaustive so we have a catch all block debug_assert_eq!(options.len() + 1, target_blocks.len()); TerminatorKind::SwitchInt { - discr: Operand::Copy(*place), + discr: Operand::Copy(place), switch_ty, values: options.clone().into(), targets: target_blocks, @@ -267,7 +262,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { if let [success, fail] = *make_target_blocks(self) { assert_eq!(value.ty, ty); let expect = self.literal_operand(test.span, value); - let val = Operand::Copy(*place); + let val = Operand::Copy(place); self.compare(block, success, fail, source_info, BinOp::Eq, expect, val); } else { bug!("`TestKind::Eq` should have two target blocks"); @@ -282,7 +277,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons. let lo = self.literal_operand(test.span, lo); let hi = self.literal_operand(test.span, hi); - let val = Operand::Copy(*place); + let val = Operand::Copy(place); if let [success, fail] = *target_blocks { self.compare( @@ -311,7 +306,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let actual = self.temp(usize_ty, test.span); // actual = len(place) - self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(*place)); + self.cfg.push_assign(block, source_info, &actual, Rvalue::Len(place)); // expected = let expected = self.push_usize(block, source_info, len); @@ -367,13 +362,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { make_target_blocks: impl FnOnce(&mut Self) -> Vec, source_info: SourceInfo, value: &'tcx ty::Const<'tcx>, - place: &Place<'tcx>, + place: Place<'tcx>, mut ty: Ty<'tcx>, ) { use rustc::middle::lang_items::EqTraitLangItem; let mut expect = self.literal_operand(source_info.span, value); - let mut val = Operand::Copy(*place); + let mut val = Operand::Copy(place); // If we're using `b"..."` as a pattern, we need to insert an // unsizing coercion, as the byte string has the type `&[u8; N]`. @@ -751,8 +746,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let downcast_place = tcx.mk_place_elem(match_pair.place, elem); // `(x as Variant)` let consequent_match_pairs = subpatterns.iter().map(|subpattern| { // e.g., `(x as Variant).0` - let place = - tcx.mk_place_field(downcast_place.clone(), subpattern.field, subpattern.pattern.ty); + let place = tcx.mk_place_field(downcast_place, subpattern.field, subpattern.pattern.ty); // e.g., `(x as Variant).0 @ P1` MatchPair::new(place, &subpattern.pattern) }); diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs index 1f536b63a394a..7e51f7aafe4b3 100644 --- a/src/librustc_mir_build/build/mod.rs +++ b/src/librustc_mir_build/build/mod.rs @@ -899,7 +899,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { matches::ArmHasGuard(false), Some((Some(&place), span)), ); - unpack!(block = self.place_into_pattern(block, pattern, &place, false)); + unpack!(block = self.place_into_pattern(block, pattern, place, false)); } } self.source_scope = original_source_scope; From 8dbbe4d14467d95d89ca3dff9054522f32cc12e8 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sat, 25 Jan 2020 00:10:40 +0000 Subject: [PATCH 13/13] Avoid scheduling repeated `StorageDead`s Also add some comments --- src/librustc_mir_build/build/block.rs | 2 +- src/librustc_mir_build/build/matches/mod.rs | 62 ++++++++++++++----- .../build/matches/simplify.rs | 3 + 3 files changed, 52 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir_build/build/block.rs b/src/librustc_mir_build/build/block.rs index c517d3113c659..df5526ad76281 100644 --- a/src/librustc_mir_build/build/block.rs +++ b/src/librustc_mir_build/build/block.rs @@ -149,7 +149,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &pattern, UserTypeProjections::none(), &mut |this, _, _, _, node, span, _, _| { - this.storage_live_binding(block, node, span, OutsideGuard); + this.storage_live_binding(block, node, span, OutsideGuard, true); this.schedule_drop_for_binding(node, span, OutsideGuard); }, ) diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs index 7ab9129398975..f900ae45b94d6 100644 --- a/src/librustc_mir_build/build/matches/mod.rs +++ b/src/librustc_mir_build/build/matches/mod.rs @@ -274,9 +274,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { end_block.unit() } - /// Binds the variables and ascribes types for a given `match` arm. + /// Binds the variables and ascribes types for a given `match` arm or + /// `let` binding. /// /// Also check if the guard matches, if it's provided. + /// `arm_scope` should be `Some` if and only if this is called for a + /// `match` arm. fn bind_pattern( &mut self, outer_source_info: SourceInfo, @@ -298,6 +301,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { true, ) } else { + // It's helpful to avoid scheduling drops multiple times to save + // drop elaboration from having to clean up the extra drops. + // + // If we are in a `let` then we only schedule drops for the first + // candidate. + // + // If we're in a `match` arm then we could have a case like so: + // + // Ok(x) | Err(x) if return => { /* ... */ } + // + // In this case we don't want a drop of `x` scheduled when we + // return: it isn't bound by move until right before enter the arm. + // To handle this we instead unschedule it's drop after each time + // we lower the guard. let target_block = self.cfg.start_new_block(); let mut schedule_drops = true; // We keep a stack of all of the bindings and type asciptions @@ -308,7 +325,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { &mut Vec::new(), &mut |leaf_candidate, parent_bindings| { if let Some(arm_scope) = arm_scope { - // Avoid scheduling drops multiple times by unscheduling drops. self.clear_top_scope(arm_scope); } let binding_end = self.bind_and_guard_matched_candidate( @@ -320,9 +336,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { schedule_drops, ); if arm_scope.is_none() { - // If we aren't in a match, then our bindings may not be - // the only thing in the top scope, so only schedule - // them to drop for the first pattern instead. schedule_drops = false; } self.cfg.goto(binding_end, outer_source_info, target_block); @@ -350,7 +363,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Optimize the case of `let x = ...` to write directly into `x` PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => { let place = - self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); + self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); unpack!(block = self.into(&place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. @@ -385,7 +398,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { hair::pattern::Ascription { user_ty: pat_ascription_ty, variance: _, user_ty_span }, } => { let place = - self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard); + self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true); unpack!(block = self.into(&place, block, initializer)); // Inject a fake read, see comments on `FakeReadCause::ForLet`. @@ -532,12 +545,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { var: HirId, span: Span, for_guard: ForGuard, + schedule_drop: bool, ) -> Place<'tcx> { let local_id = self.var_local_id(var, for_guard); let source_info = self.source_info(span); self.cfg.push(block, Statement { source_info, kind: StatementKind::StorageLive(local_id) }); let region_scope = self.hir.region_scope_tree.var_scope(var.local_id); - self.schedule_drop(span, region_scope, local_id, DropKind::Storage); + if schedule_drop { + self.schedule_drop(span, region_scope, local_id, DropKind::Storage); + } Place::from(local_id) } @@ -1060,25 +1076,31 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// | /// +----------------------------------------+------------------------------------+ /// | | | + /// V V V /// [ P matches ] [ Q matches ] [ otherwise ] /// | | | + /// V V | /// [ match R, S ] [ match R, S ] | /// | | | /// +--------------+------------+ +--------------+------------+ | /// | | | | | | | + /// V V V V V V | /// [ R matches ] [ S matches ] [otherwise ] [ R matches ] [ S matches ] [otherwise ] | /// | | | | | | | /// +--------------+------------|------------+--------------+ | | /// | | | | /// | +----------------------------------------+--------+ /// | | + /// V V /// [ Success ] [ Failure ] /// ``` /// /// In practice there are some complications: /// /// * If there's a guard, then the otherwise branch of the first match on - /// `R | S` goes to a test for whether `Q` matches. + /// `R | S` goes to a test for whether `Q` matches, and the control flow + /// doesn't merge into a single success block until after the guard is + /// tested. /// * If neither `P` or `Q` has any bindings or type ascriptions and there /// isn't a match guard, then we create a smaller CFG like: /// @@ -1658,7 +1680,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .flat_map(|(bindings, _)| bindings) .chain(&candidate.bindings); - self.bind_matched_candidate_for_guard(block, bindings.clone()); + self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone()); let guard_frame = GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)).collect(), }; @@ -1807,6 +1829,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { fn bind_matched_candidate_for_guard<'b>( &mut self, block: BasicBlock, + schedule_drops: bool, bindings: impl IntoIterator>, ) where 'tcx: 'b, @@ -1825,8 +1848,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // a reference R: &T pointing to the location matched by // the pattern, and every occurrence of P within a guard // denotes *R. - let ref_for_guard = - self.storage_live_binding(block, binding.var_id, binding.span, RefWithinGuard); + let ref_for_guard = self.storage_live_binding( + block, + binding.var_id, + binding.span, + RefWithinGuard, + schedule_drops, + ); match binding.binding_mode { BindingMode::ByValue => { let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source); @@ -1838,6 +1866,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { binding.var_id, binding.span, OutsideGuard, + schedule_drops, ); let rvalue = Rvalue::Ref(re_erased, borrow_kind, binding.source); @@ -1863,8 +1892,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // Assign each of the bindings. This may trigger moves out of the candidate. for binding in bindings { let source_info = self.source_info(binding.span); - let local = - self.storage_live_binding(block, binding.var_id, binding.span, OutsideGuard); + let local = self.storage_live_binding( + block, + binding.var_id, + binding.span, + OutsideGuard, + schedule_drops, + ); if schedule_drops { self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard); } diff --git a/src/librustc_mir_build/build/matches/simplify.rs b/src/librustc_mir_build/build/matches/simplify.rs index 4213a30f2d8c1..56aa150dd37d2 100644 --- a/src/librustc_mir_build/build/matches/simplify.rs +++ b/src/librustc_mir_build/build/matches/simplify.rs @@ -75,6 +75,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } + /// Given `candidate` that has a single or-pattern for its match-pairs, + /// creates a fresh candidate for each of its input subpatterns passed via + /// `pats`. fn create_or_subcandidates<'pat>( &mut self, candidate: &Candidate<'pat, 'tcx>,