From 4dc5697bd2799916f342810d054f3bcc00471689 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 20:41:38 +0200 Subject: [PATCH 01/18] Add some slice-pattern exhaustiveness tests --- .../ui/pattern/usefulness/slice-patterns.rs | 96 ++++++++++++++ .../pattern/usefulness/slice-patterns.stderr | 117 ++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 src/test/ui/pattern/usefulness/slice-patterns.rs create mode 100644 src/test/ui/pattern/usefulness/slice-patterns.stderr diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs new file mode 100644 index 0000000000000..0493646ff3bba --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -0,0 +1,96 @@ +#![feature(slice_patterns)] +#![deny(unreachable_patterns)] + +fn main() { + let s: &[bool] = &[true; 0]; + let s0: &[bool; 0] = &[]; + let s1: &[bool; 1] = &[false; 1]; + let s2: &[bool; 2] = &[false; 2]; + let s3: &[bool; 3] = &[false; 3]; + + let [] = s0; + let [_] = s1; + let [_, _] = s2; + + let [..] = s; + let [..] = s0; + let [..] = s1; + let [..] = s2; + let [..] = s3; + + let [_, _, ..] = s2; + let [_, .., _] = s2; + let [.., _, _] = s2; + + match s1 { + [true, ..] => {} + [.., false] => {} + } + match s2 { + //~^ ERROR `&[false, true]` not covered + [true, ..] => {} + [.., false] => {} + } + match s3 { + //~^ ERROR `&[false, _, true]` not covered + [true, ..] => {} + [.., false] => {} + } + match s { + //~^ ERROR `&[false, true]` not covered + [] => {} + [true, ..] => {} + [.., false] => {} + } + + match s3 { + //~^ ERROR `&[false, _, _]` not covered + [true, .., true] => {} + } + match s { + //~^ ERROR `&[_]` not covered + [] => {} + } + match s { + //~^ ERROR `&[_, _]` not covered + [] => {} + [_] => {} + } + match s { + //~^ ERROR `&[false]` not covered + [] => {} + [true, ..] => {} + } + match s { + //~^ ERROR `&[false, _]` not covered + [] => {} + [_] => {} + [true, ..] => {} + } + match s { + //~^ ERROR `&[_, false]` not covered + [] => {} + [_] => {} + [.., true] => {} + } + + match s { + [true, ..] => {} + [true, ..] => {} //~ ERROR unreachable pattern + [true] => {} //~ ERROR unreachable pattern + [..] => {} + } + match s { + [.., true] => {} + [.., true] => {} //~ ERROR unreachable pattern + [true] => {} //~ ERROR unreachable pattern + [..] => {} + } + match s { + [false, .., true] => {} + [false, .., true] => {} //~ ERROR unreachable pattern + [false, true] => {} //~ ERROR unreachable pattern + [false] => {} + [..] => {} + } +} diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr new file mode 100644 index 0000000000000..18c73330fcdc1 --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -0,0 +1,117 @@ +error[E0004]: non-exhaustive patterns: `&[false, true]` not covered + --> $DIR/slice-patterns.rs:29:11 + | +LL | match s2 { + | ^^ pattern `&[false, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, _, true]` not covered + --> $DIR/slice-patterns.rs:34:11 + | +LL | match s3 { + | ^^ pattern `&[false, _, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, true]` not covered + --> $DIR/slice-patterns.rs:39:11 + | +LL | match s { + | ^ pattern `&[false, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, _, _]` not covered + --> $DIR/slice-patterns.rs:46:11 + | +LL | match s3 { + | ^^ pattern `&[false, _, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[_]` not covered + --> $DIR/slice-patterns.rs:50:11 + | +LL | match s { + | ^ pattern `&[_]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[_, _]` not covered + --> $DIR/slice-patterns.rs:54:11 + | +LL | match s { + | ^ pattern `&[_, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false]` not covered + --> $DIR/slice-patterns.rs:59:11 + | +LL | match s { + | ^ pattern `&[false]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, _]` not covered + --> $DIR/slice-patterns.rs:64:11 + | +LL | match s { + | ^ pattern `&[false, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[_, false]` not covered + --> $DIR/slice-patterns.rs:70:11 + | +LL | match s { + | ^ pattern `&[_, false]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: unreachable pattern + --> $DIR/slice-patterns.rs:79:9 + | +LL | [true, ..] => {} + | ^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/slice-patterns.rs:2:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:80:9 + | +LL | [true] => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:85:9 + | +LL | [.., true] => {} + | ^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:86:9 + | +LL | [true] => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:91:9 + | +LL | [false, .., true] => {} + | ^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:92:9 + | +LL | [false, true] => {} + | ^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0004`. From 235b379928b03e6a55197300b564387236c84667 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 21:24:58 +0200 Subject: [PATCH 02/18] Rename Constructor::Slice to FixedLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8989d2e15de99..057fea0146d6a 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -587,7 +587,7 @@ enum Constructor<'tcx> { /// Ranges of literal values (`2..=5` and `2..5`). ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span), /// Array patterns of length n. - Slice(u64), + FixedLenSlice(u64), } // Ignore spans when comparing, they don't carry semantic information as they are only for lints. @@ -601,7 +601,7 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { Constructor::ConstantRange(a_start, a_end, a_ty, a_range_end, _), Constructor::ConstantRange(b_start, b_end, b_ty, b_range_end, _), ) => a_start == b_start && a_end == b_end && a_ty == b_ty && a_range_end == b_range_end, - (Constructor::Slice(a), Constructor::Slice(b)) => a == b, + (Constructor::FixedLenSlice(a), Constructor::FixedLenSlice(b)) => a == b, _ => false, } } @@ -610,7 +610,7 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { - Slice { .. } => true, + FixedLenSlice { .. } => true, _ => false, } } @@ -644,7 +644,7 @@ impl<'tcx> Constructor<'tcx> { ty::Const::from_bits(tcx, *hi, ty), ) } - Constructor::Slice(val) => format!("[{}]", val), + Constructor::FixedLenSlice(val) => format!("[{}]", val), _ => bug!("bad constructor being displayed: `{:?}", self), } } @@ -707,7 +707,7 @@ impl<'tcx> Constructor<'tcx> { match ty.kind { ty::Tuple(ref fs) => fs.len() as u64, ty::Slice(..) | ty::Array(..) => match *self { - Slice(length) => length, + FixedLenSlice(length) => length, ConstantValue(..) => 0, _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, @@ -980,14 +980,14 @@ fn all_constructors<'a, 'tcx>( .collect(), ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env); - if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![Slice(len)] } + if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![FixedLenSlice(len)] } } // Treat arrays of a constant but unknown length like slices. ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => { if cx.is_uninhabited(sub_ty) { - vec![Slice(0)] + vec![FixedLenSlice(0)] } else { - (0..pcx.max_slice_length + 1).map(|length| Slice(length)).collect() + (0..pcx.max_slice_length + 1).map(|length| FixedLenSlice(length)).collect() } } ty::Adt(def, substs) if def.is_enum() => def @@ -1711,15 +1711,17 @@ fn pat_constructors<'tcx>( pat.span, )]), PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => Some(vec![Slice(length.eval_usize(cx.tcx, cx.param_env))]), + ty::Array(_, length) => { + Some(vec![FixedLenSlice(length.eval_usize(cx.tcx, cx.param_env))]) + } _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let pat_len = prefix.len() as u64 + suffix.len() as u64; if slice.is_some() { - Some((pat_len..pcx.max_slice_length + 1).map(Slice).collect()) + Some((pat_len..pcx.max_slice_length + 1).map(FixedLenSlice).collect()) } else { - Some(vec![Slice(pat_len)]) + Some(vec![FixedLenSlice(pat_len)]) } } PatKind::Or { .. } => { @@ -1741,7 +1743,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( match ty.kind { ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), ty::Slice(ty) | ty::Array(ty, _) => match *ctor { - Slice(length) => (0..length).map(|_| ty).collect(), + FixedLenSlice(length) => (0..length).map(|_| ty).collect(), ConstantValue(..) => vec![], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, @@ -2238,7 +2240,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( PatKind::Array { ref prefix, ref slice, ref suffix } | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { - Slice(..) => { + FixedLenSlice(..) => { let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { From d2b7711e8e955649e532d0e78279876178a51983 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Nov 2019 18:23:01 +0000 Subject: [PATCH 03/18] Match on self first in subtract_constructor --- src/librustc_mir/hair/pattern/_match.rs | 54 +++++++++++++++---------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 057fea0146d6a..f7b6203c15bac 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -657,31 +657,43 @@ impl<'tcx> Constructor<'tcx> { param_env: ty::ParamEnv<'tcx>, other_ctors: &Vec>, ) -> Vec> { - let mut refined_ctors = vec![self.clone()]; - for other_ctor in other_ctors { - if other_ctor == self { - // If a constructor appears in a `match` arm, we can - // eliminate it straight away. - refined_ctors = vec![] - } else if let Some(interval) = IntRange::from_ctor(tcx, param_env, other_ctor) { - // Refine the required constructors for the type by subtracting - // the range defined by the current constructor pattern. - refined_ctors = interval.subtract_from(tcx, param_env, refined_ctors); + match self { + // Those constructors can only match themselves. + Single | Variant(_) | FixedLenSlice(_) => { + if other_ctors.iter().any(|c| c == self) { + vec![] + } else { + vec![self.clone()] + } } + ConstantRange(..) | ConstantValue(..) => { + let mut remaining_ctors = vec![self.clone()]; + for other_ctor in other_ctors { + if other_ctor == self { + // If a constructor appears in a `match` arm, we can + // eliminate it straight away. + remaining_ctors = vec![] + } else if let Some(interval) = IntRange::from_ctor(tcx, param_env, other_ctor) { + // Refine the required constructors for the type by subtracting + // the range defined by the current constructor pattern. + remaining_ctors = interval.subtract_from(tcx, param_env, remaining_ctors); + } - // If the constructor patterns that have been considered so far - // already cover the entire range of values, then we know the - // constructor is not missing, and we can move on to the next one. - if refined_ctors.is_empty() { - break; + // If the constructor patterns that have been considered so far + // already cover the entire range of values, then we know the + // constructor is not missing, and we can move on to the next one. + if remaining_ctors.is_empty() { + break; + } + } + + // If a constructor has not been matched, then it is missing. + // We add `remaining_ctors` instead of `self`, because then we can + // provide more detailed error information about precisely which + // ranges have been omitted. + remaining_ctors } } - - // If a constructor has not been matched, then it is missing. - // We add `refined_ctors` instead of `self`, because then we can - // provide more detailed error information about precisely which - // ranges have been omitted. - refined_ctors } /// This returns one wildcard pattern for each argument to this constructor. From e092a170528c964dddb0ed9764e8593d9ac8b370 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 5 Nov 2019 17:57:55 +0000 Subject: [PATCH 04/18] Add special variable-length slice constructor --- src/librustc_mir/hair/pattern/_match.rs | 98 ++++++++++++++++++------- 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f7b6203c15bac..d0597817d32f5 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -588,6 +588,8 @@ enum Constructor<'tcx> { ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span), /// Array patterns of length n. FixedLenSlice(u64), + /// Slice patterns. Stands for any array constructor of length >= n. + VarLenSlice(u64), } // Ignore spans when comparing, they don't carry semantic information as they are only for lints. @@ -602,6 +604,7 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { Constructor::ConstantRange(b_start, b_end, b_ty, b_range_end, _), ) => a_start == b_start && a_end == b_end && a_ty == b_ty && a_range_end == b_range_end, (Constructor::FixedLenSlice(a), Constructor::FixedLenSlice(b)) => a == b, + (Constructor::VarLenSlice(a), Constructor::VarLenSlice(b)) => a == b, _ => false, } } @@ -611,6 +614,7 @@ impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { FixedLenSlice { .. } => true, + VarLenSlice { .. } => true, _ => false, } } @@ -645,6 +649,7 @@ impl<'tcx> Constructor<'tcx> { ) } Constructor::FixedLenSlice(val) => format!("[{}]", val), + Constructor::VarLenSlice(val) => format!("[{}, ..]", val), _ => bug!("bad constructor being displayed: `{:?}", self), } } @@ -653,19 +658,59 @@ impl<'tcx> Constructor<'tcx> { // anything in `other_ctors`. fn subtract_ctors( &self, + pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, other_ctors: &Vec>, ) -> Vec> { match self { // Those constructors can only match themselves. - Single | Variant(_) | FixedLenSlice(_) => { + Single | Variant(_) => { if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] } } + FixedLenSlice(_) | VarLenSlice(_) => { + let mut remaining_ctors = if let VarLenSlice(len) = self { + (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() + } else { + vec![self.clone()] + }; + + // For each used ctor, subtract from the current set of constructors. + // Naming: we remove the "neg" constructors from the "pos" ones. + // Remember, VarLenSlice(n) covers the union of FixedLenSlice from + // n to infinity. + for neg_ctor in other_ctors { + remaining_ctors = remaining_ctors + .into_iter() + .flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> { + // Compute pos_ctor \ neg_ctor + match (&pos_ctor, neg_ctor) { + (FixedLenSlice(pos_len), VarLenSlice(neg_len)) => { + if neg_len <= pos_len { + smallvec![] + } else { + smallvec![pos_ctor] + } + } + _ if pos_ctor == *neg_ctor => smallvec![], + _ => smallvec![pos_ctor], + } + }) + .collect(); + + // If the constructors that have been considered so far already cover + // the entire range of `self`, no need to look at more constructors. + if remaining_ctors.is_empty() { + break; + } + } + + remaining_ctors + } ConstantRange(..) | ConstantValue(..) => { let mut remaining_ctors = vec![self.clone()]; for other_ctor in other_ctors { @@ -719,7 +764,7 @@ impl<'tcx> Constructor<'tcx> { match ty.kind { ty::Tuple(ref fs) => fs.len() as u64, ty::Slice(..) | ty::Array(..) => match *self { - FixedLenSlice(length) => length, + FixedLenSlice(length) | VarLenSlice(length) => length, ConstantValue(..) => 0, _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, @@ -999,7 +1044,7 @@ fn all_constructors<'a, 'tcx>( if cx.is_uninhabited(sub_ty) { vec![FixedLenSlice(0)] } else { - (0..pcx.max_slice_length + 1).map(|length| FixedLenSlice(length)).collect() + vec![VarLenSlice(0)] } } ty::Adt(def, substs) if def.is_enum() => def @@ -1403,6 +1448,7 @@ impl<'tcx> IntRange<'tcx> { // A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. struct MissingConstructors<'tcx> { + pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, @@ -1411,12 +1457,13 @@ struct MissingConstructors<'tcx> { impl<'tcx> MissingConstructors<'tcx> { fn new( + pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, ) -> Self { - MissingConstructors { tcx, param_env, all_ctors, used_ctors } + MissingConstructors { pcx, tcx, param_env, all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { @@ -1435,7 +1482,7 @@ impl<'tcx> MissingConstructors<'tcx> { /// Iterate over all_ctors \ used_ctors fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { self.all_ctors.iter().flat_map(move |req_ctor| { - req_ctor.subtract_ctors(self.tcx, self.param_env, &self.used_ctors) + req_ctor.subtract_ctors(self.pcx, self.tcx, self.param_env, &self.used_ctors) }) } } @@ -1531,9 +1578,9 @@ pub fn is_useful<'p, 'a, 'tcx>( split_grouped_constructors( cx.tcx, cx.param_env, + pcx, constructors, matrix, - pcx.ty, pcx.span, Some(hir_id), ) @@ -1578,7 +1625,8 @@ pub fn is_useful<'p, 'a, 'tcx>( // non-wildcard patterns in the current column. To determine if // the set is empty, we can check that `.peek().is_none()`, so // we only fully construct them on-demand, because they're rarely used and can be big. - let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); + let missing_ctors = + MissingConstructors::new(pcx, cx.tcx, cx.param_env, all_ctors, used_ctors); debug!( "missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", @@ -1595,19 +1643,13 @@ pub fn is_useful<'p, 'a, 'tcx>( if missing_ctors.is_empty() && !is_non_exhaustive { let (all_ctors, _) = missing_ctors.into_inner(); - split_grouped_constructors( - cx.tcx, - cx.param_env, - all_ctors, - matrix, - pcx.ty, - DUMMY_SP, - None, - ) - .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) + split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, DUMMY_SP, None) + .into_iter() + .map(|c| { + is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id) + }) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) } else { let matrix = matrix.specialize_wildcard(); let v = v.to_tail(); @@ -1731,7 +1773,7 @@ fn pat_constructors<'tcx>( PatKind::Slice { ref prefix, ref slice, ref suffix } => { let pat_len = prefix.len() as u64 + suffix.len() as u64; if slice.is_some() { - Some((pat_len..pcx.max_slice_length + 1).map(FixedLenSlice).collect()) + Some(vec![VarLenSlice(pat_len)]) } else { Some(vec![FixedLenSlice(pat_len)]) } @@ -1755,7 +1797,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( match ty.kind { ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), ty::Slice(ty) | ty::Array(ty, _) => match *ctor { - FixedLenSlice(length) => (0..length).map(|_| ty).collect(), + FixedLenSlice(length) | VarLenSlice(length) => (0..length).map(|_| ty).collect(), ConstantValue(..) => vec![], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, @@ -1914,21 +1956,22 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) /// /// `hir_id` is `None` when we're evaluating the wildcard pattern, do not lint for overlapping in /// ranges that case. +/// +/// This also splits variable-length slices into fixed-length slices. fn split_grouped_constructors<'p, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, + pcx: PatCtxt<'tcx>, ctors: Vec>, matrix: &Matrix<'p, 'tcx>, - ty: Ty<'tcx>, span: Span, hir_id: Option, ) -> Vec> { + let ty = pcx.ty; let mut split_ctors = Vec::with_capacity(ctors.len()); for ctor in ctors.into_iter() { match ctor { - // For now, only ranges may denote groups of "subconstructors", so we only need to - // special-case constant ranges. ConstantRange(..) if should_treat_range_exhaustively(tcx, &ctor) => { // We only care about finding all the subranges within the range of the constructor // range. Anything else is irrelevant, because it is guaranteed to result in @@ -2010,6 +2053,9 @@ fn split_grouped_constructors<'p, 'tcx>( split_ctors.push(IntRange::range_to_ctor(tcx, ty, range, span)); } } + VarLenSlice(len) => { + split_ctors.extend((len..pcx.max_slice_length + 1).map(FixedLenSlice)) + } // Any other constructor can be used unchanged. _ => split_ctors.push(ctor), } @@ -2252,7 +2298,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( PatKind::Array { ref prefix, ref slice, ref suffix } | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { - FixedLenSlice(..) => { + FixedLenSlice(..) | VarLenSlice(..) => { let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { From 909ec370286b193f67e738e7b9d6437cf32c014c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Nov 2019 21:22:14 +0000 Subject: [PATCH 05/18] Simpler code path for subtracting from FixedLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index d0597817d32f5..31984daa5df63 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -672,7 +672,15 @@ impl<'tcx> Constructor<'tcx> { vec![self.clone()] } } - FixedLenSlice(_) | VarLenSlice(_) => { + FixedLenSlice(self_len) => { + let overlaps = |c: &Constructor<'_>| match c { + FixedLenSlice(other_len) => other_len == self_len, + VarLenSlice(other_len) => other_len <= self_len, + _ => false, + }; + if other_ctors.iter().any(overlaps) { vec![] } else { vec![self.clone()] } + } + VarLenSlice(_) => { let mut remaining_ctors = if let VarLenSlice(len) = self { (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() } else { From b66973043e555f3a24a50a227db76b0a069ea037 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Nov 2019 23:10:33 +0000 Subject: [PATCH 06/18] Don't use max_slice_length when subtracting from VarLenSlice This alters error messages slightly, but that'll be improved later --- src/librustc_mir/hair/pattern/_match.rs | 33 ++++++++++++------- .../match-byte-array-patterns-2.stderr | 4 +-- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 31984daa5df63..902d9b610ce33 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -658,7 +658,6 @@ impl<'tcx> Constructor<'tcx> { // anything in `other_ctors`. fn subtract_ctors( &self, - pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, other_ctors: &Vec>, @@ -681,11 +680,7 @@ impl<'tcx> Constructor<'tcx> { if other_ctors.iter().any(overlaps) { vec![] } else { vec![self.clone()] } } VarLenSlice(_) => { - let mut remaining_ctors = if let VarLenSlice(len) = self { - (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() - } else { - vec![self.clone()] - }; + let mut remaining_ctors = vec![self.clone()]; // For each used ctor, subtract from the current set of constructors. // Naming: we remove the "neg" constructors from the "pos" ones. @@ -704,6 +699,23 @@ impl<'tcx> Constructor<'tcx> { smallvec![pos_ctor] } } + (VarLenSlice(pos_len), VarLenSlice(neg_len)) => { + if neg_len <= pos_len { + smallvec![] + } else { + (*pos_len..*neg_len).map(FixedLenSlice).collect() + } + } + (VarLenSlice(pos_len), FixedLenSlice(neg_len)) => { + if neg_len < pos_len { + smallvec![pos_ctor] + } else { + (*pos_len..*neg_len) + .map(FixedLenSlice) + .chain(Some(VarLenSlice(neg_len + 1))) + .collect() + } + } _ if pos_ctor == *neg_ctor => smallvec![], _ => smallvec![pos_ctor], } @@ -1456,7 +1468,6 @@ impl<'tcx> IntRange<'tcx> { // A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. struct MissingConstructors<'tcx> { - pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, @@ -1465,13 +1476,12 @@ struct MissingConstructors<'tcx> { impl<'tcx> MissingConstructors<'tcx> { fn new( - pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, ) -> Self { - MissingConstructors { pcx, tcx, param_env, all_ctors, used_ctors } + MissingConstructors { tcx, param_env, all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { @@ -1490,7 +1500,7 @@ impl<'tcx> MissingConstructors<'tcx> { /// Iterate over all_ctors \ used_ctors fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { self.all_ctors.iter().flat_map(move |req_ctor| { - req_ctor.subtract_ctors(self.pcx, self.tcx, self.param_env, &self.used_ctors) + req_ctor.subtract_ctors(self.tcx, self.param_env, &self.used_ctors) }) } } @@ -1633,8 +1643,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // non-wildcard patterns in the current column. To determine if // the set is empty, we can check that `.peek().is_none()`, so // we only fully construct them on-demand, because they're rarely used and can be big. - let missing_ctors = - MissingConstructors::new(pcx, cx.tcx, cx.param_env, all_ctors, used_ctors); + let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); debug!( "missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index d53e2e25b3dbd..9938c9c284d1c 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -6,11 +6,11 @@ LL | match buf { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 3 more not covered +error[E0004]: non-exhaustive patterns: `&[]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 3 more not covered + | ^^^ pattern `&[]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 65bc67e8d9922c450ee81971cd1bab030fcf0a14 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Nov 2019 23:11:04 +0000 Subject: [PATCH 07/18] Make exhaustiveness error message more consistent for slice patterns This improves error messages by indicating when slices above a certain lengths have not been matched. Previously, we would only report examples of such lengths, but of course never all of them. --- src/librustc_mir/hair/pattern/_match.rs | 14 +++++++++++--- src/test/ui/consts/const_let_refutable.stderr | 4 ++-- .../usefulness/match-byte-array-patterns-2.stderr | 4 ++-- .../ui/pattern/usefulness/non-exhaustive-match.rs | 2 +- .../pattern/usefulness/non-exhaustive-match.stderr | 4 ++-- src/test/ui/pattern/usefulness/slice-patterns.rs | 4 ++-- .../ui/pattern/usefulness/slice-patterns.stderr | 8 ++++---- .../uninhabited-matches-feature-gated.stderr | 4 ++-- 8 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 902d9b610ce33..c70a7ea1f37dd 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -841,9 +841,17 @@ impl<'tcx> Constructor<'tcx> { ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() }, - ty::Slice(_) | ty::Array(..) => { - PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } - } + ty::Slice(_) | ty::Array(..) => match self { + FixedLenSlice(_) => { + PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } + } + VarLenSlice(_) => { + let prefix = subpatterns.collect(); + let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; + PatKind::Slice { prefix, slice: Some(wild), suffix: vec![] } + } + _ => bug!("bad slice pattern {:?} {:?}", self, ty), + }, _ => match *self { ConstantValue(value, _) => PatKind::Constant { value }, diff --git a/src/test/ui/consts/const_let_refutable.stderr b/src/test/ui/consts/const_let_refutable.stderr index 7f15f02d4d37b..9acb4ad9cbbe5 100644 --- a/src/test/ui/consts/const_let_refutable.stderr +++ b/src/test/ui/consts/const_let_refutable.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _]` not covered +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 + | ^^^^^^ 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 diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index 9938c9c284d1c..6e52072e3bfec 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -6,11 +6,11 @@ LL | match buf { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[]` not covered +error[E0004]: non-exhaustive patterns: `&[..]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ pattern `&[]` not covered + | ^^^ pattern `&[..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs index 0e5a9203c5f80..bfca5352353a7 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs @@ -44,7 +44,7 @@ fn main() { } let vec = vec![0.5f32]; let vec: &[f32] = &vec; - match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered + match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _, ..]` not covered [0.1, 0.2, 0.3] => (), [0.1, 0.2] => (), [0.1] => (), diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr index 5dba05e16427a..577867e4e7122 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -66,11 +66,11 @@ LL | match *vec { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `[_, _, _, _]` not covered +error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered --> $DIR/non-exhaustive-match.rs:47:11 | LL | match *vec { - | ^^^^ pattern `[_, _, _, _]` not covered + | ^^^^ pattern `[_, _, _, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs index 0493646ff3bba..97086c4d75ddc 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -48,11 +48,11 @@ fn main() { [true, .., true] => {} } match s { - //~^ ERROR `&[_]` not covered + //~^ ERROR `&[_, ..]` not covered [] => {} } match s { - //~^ ERROR `&[_, _]` not covered + //~^ ERROR `&[_, _, ..]` not covered [] => {} [_] => {} } diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr index 18c73330fcdc1..3cea068543ebd 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -30,19 +30,19 @@ LL | match s3 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_]` not covered +error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered --> $DIR/slice-patterns.rs:50:11 | LL | match s { - | ^ pattern `&[_]` not covered + | ^ pattern `&[_, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_, _]` not covered +error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered --> $DIR/slice-patterns.rs:54:11 | LL | match s { - | ^ pattern `&[_, _]` not covered + | ^ pattern `&[_, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index a49344e45cec6..7af6075262c6d 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -30,11 +30,11 @@ LL | let _ = match x {}; | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_]` not covered +error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered --> $DIR/uninhabited-matches-feature-gated.rs:21:19 | LL | let _ = match x { - | ^ pattern `&[_]` not covered + | ^ pattern `&[_, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 58de9d9a137178856bca163aa16315bba01723bc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 22:57:52 +0100 Subject: [PATCH 08/18] Add some test cases --- .../ui/pattern/usefulness/slice-patterns.rs | 14 ++++++++++++++ .../pattern/usefulness/slice-patterns.stderr | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs index 97086c4d75ddc..e11f11ba7524d 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -93,4 +93,18 @@ fn main() { [false] => {} [..] => {} } + match s { + //~^ ERROR `&[_, _, true]` not covered + [] => {} + [_] => {} + [_, _] => {} + [.., false] => {} + } + match s { + //~^ ERROR `&[true, _, _]` not covered + [] => {} + [_] => {} + [_, _] => {} + [false, .., false] => {} + } } diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr index 3cea068543ebd..666e16627c57e 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -112,6 +112,22 @@ error: unreachable pattern LL | [false, true] => {} | ^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error[E0004]: non-exhaustive patterns: `&[_, _, true]` not covered + --> $DIR/slice-patterns.rs:96:11 + | +LL | match s { + | ^ pattern `&[_, _, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[true, _, _]` not covered + --> $DIR/slice-patterns.rs:103:11 + | +LL | match s { + | ^ pattern `&[true, _, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0004`. From d7044f23e9c1b5e59a10fdf66d23127a00fedbc3 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 5 Nov 2019 12:04:37 +0000 Subject: [PATCH 09/18] Store both prefix and suffix length in VarLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 72 +++++++++++++++---------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index c70a7ea1f37dd..68486510b764b 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -588,8 +588,8 @@ enum Constructor<'tcx> { ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span), /// Array patterns of length n. FixedLenSlice(u64), - /// Slice patterns. Stands for any array constructor of length >= n. - VarLenSlice(u64), + /// Slice patterns. Captures any array constructor of length >= i+j. + VarLenSlice(u64, u64), } // Ignore spans when comparing, they don't carry semantic information as they are only for lints. @@ -604,7 +604,10 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { Constructor::ConstantRange(b_start, b_end, b_ty, b_range_end, _), ) => a_start == b_start && a_end == b_end && a_ty == b_ty && a_range_end == b_range_end, (Constructor::FixedLenSlice(a), Constructor::FixedLenSlice(b)) => a == b, - (Constructor::VarLenSlice(a), Constructor::VarLenSlice(b)) => a == b, + ( + Constructor::VarLenSlice(a_prefix, a_suffix), + Constructor::VarLenSlice(b_prefix, b_suffix), + ) => a_prefix == b_prefix && a_suffix == b_suffix, _ => false, } } @@ -649,7 +652,7 @@ impl<'tcx> Constructor<'tcx> { ) } Constructor::FixedLenSlice(val) => format!("[{}]", val), - Constructor::VarLenSlice(val) => format!("[{}, ..]", val), + Constructor::VarLenSlice(prefix, suffix) => format!("[{}, .., {}]", prefix, suffix), _ => bug!("bad constructor being displayed: `{:?}", self), } } @@ -662,7 +665,7 @@ impl<'tcx> Constructor<'tcx> { param_env: ty::ParamEnv<'tcx>, other_ctors: &Vec>, ) -> Vec> { - match self { + match *self { // Those constructors can only match themselves. Single | Variant(_) => { if other_ctors.iter().any(|c| c == self) { @@ -672,47 +675,58 @@ impl<'tcx> Constructor<'tcx> { } } FixedLenSlice(self_len) => { - let overlaps = |c: &Constructor<'_>| match c { + let overlaps = |c: &Constructor<'_>| match *c { FixedLenSlice(other_len) => other_len == self_len, - VarLenSlice(other_len) => other_len <= self_len, + VarLenSlice(prefix, suffix) => prefix + suffix <= self_len, _ => false, }; if other_ctors.iter().any(overlaps) { vec![] } else { vec![self.clone()] } } - VarLenSlice(_) => { + VarLenSlice(..) => { let mut remaining_ctors = vec![self.clone()]; // For each used ctor, subtract from the current set of constructors. // Naming: we remove the "neg" constructors from the "pos" ones. - // Remember, VarLenSlice(n) covers the union of FixedLenSlice from - // n to infinity. + // Remember, VarLenSlice(i, j) covers the union of FixedLenSlice from + // i+j to infinity. for neg_ctor in other_ctors { remaining_ctors = remaining_ctors .into_iter() .flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> { // Compute pos_ctor \ neg_ctor match (&pos_ctor, neg_ctor) { - (FixedLenSlice(pos_len), VarLenSlice(neg_len)) => { + (&FixedLenSlice(pos_len), &VarLenSlice(neg_prefix, neg_suffix)) => { + let neg_len = neg_prefix + neg_suffix; if neg_len <= pos_len { smallvec![] } else { smallvec![pos_ctor] } } - (VarLenSlice(pos_len), VarLenSlice(neg_len)) => { + ( + &VarLenSlice(pos_prefix, pos_suffix), + &VarLenSlice(neg_prefix, neg_suffix), + ) => { + let neg_len = neg_prefix + neg_suffix; + let pos_len = pos_prefix + pos_suffix; if neg_len <= pos_len { smallvec![] } else { - (*pos_len..*neg_len).map(FixedLenSlice).collect() + (pos_len..neg_len).map(FixedLenSlice).collect() } } - (VarLenSlice(pos_len), FixedLenSlice(neg_len)) => { + (&VarLenSlice(pos_prefix, pos_suffix), &FixedLenSlice(neg_len)) => { + let pos_len = pos_prefix + pos_suffix; if neg_len < pos_len { smallvec![pos_ctor] } else { - (*pos_len..*neg_len) + (pos_len..neg_len) .map(FixedLenSlice) - .chain(Some(VarLenSlice(neg_len + 1))) + // We know neg_len + 1 >= pos_len >= pos_suffix + .chain(Some(VarLenSlice( + neg_len + 1 - pos_suffix, + pos_suffix, + ))) .collect() } } @@ -784,7 +798,8 @@ impl<'tcx> Constructor<'tcx> { match ty.kind { ty::Tuple(ref fs) => fs.len() as u64, ty::Slice(..) | ty::Array(..) => match *self { - FixedLenSlice(length) | VarLenSlice(length) => length, + FixedLenSlice(length) => length, + VarLenSlice(prefix, suffix) => prefix + suffix, ConstantValue(..) => 0, _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, @@ -845,10 +860,11 @@ impl<'tcx> Constructor<'tcx> { FixedLenSlice(_) => { PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } } - VarLenSlice(_) => { - let prefix = subpatterns.collect(); + VarLenSlice(prefix_len, _suffix_len) => { + let prefix = subpatterns.by_ref().take(*prefix_len as usize).collect(); + let suffix = subpatterns.collect(); let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; - PatKind::Slice { prefix, slice: Some(wild), suffix: vec![] } + PatKind::Slice { prefix, slice: Some(wild), suffix } } _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, @@ -1072,7 +1088,7 @@ fn all_constructors<'a, 'tcx>( if cx.is_uninhabited(sub_ty) { vec![FixedLenSlice(0)] } else { - vec![VarLenSlice(0)] + vec![VarLenSlice(0, 0)] } } ty::Adt(def, substs) if def.is_enum() => def @@ -1796,11 +1812,12 @@ fn pat_constructors<'tcx>( _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { - let pat_len = prefix.len() as u64 + suffix.len() as u64; + let prefix = prefix.len() as u64; + let suffix = suffix.len() as u64; if slice.is_some() { - Some(vec![VarLenSlice(pat_len)]) + Some(vec![VarLenSlice(prefix, suffix)]) } else { - Some(vec![FixedLenSlice(pat_len)]) + Some(vec![FixedLenSlice(prefix + suffix)]) } } PatKind::Or { .. } => { @@ -1822,7 +1839,8 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( match ty.kind { ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), ty::Slice(ty) | ty::Array(ty, _) => match *ctor { - FixedLenSlice(length) | VarLenSlice(length) => (0..length).map(|_| ty).collect(), + FixedLenSlice(length) => (0..length).map(|_| ty).collect(), + VarLenSlice(prefix, suffix) => (0..prefix + suffix).map(|_| ty).collect(), ConstantValue(..) => vec![], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, @@ -2078,8 +2096,8 @@ fn split_grouped_constructors<'p, 'tcx>( split_ctors.push(IntRange::range_to_ctor(tcx, ty, range, span)); } } - VarLenSlice(len) => { - split_ctors.extend((len..pcx.max_slice_length + 1).map(FixedLenSlice)) + VarLenSlice(prefix, suffix) => { + split_ctors.extend((prefix + suffix..pcx.max_slice_length + 1).map(FixedLenSlice)) } // Any other constructor can be used unchanged. _ => split_ctors.push(ctor), From 149792b6080f40875c0072aae378a0eb31d23df0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 5 Nov 2019 16:16:47 +0000 Subject: [PATCH 10/18] pat_constructors returns at most one constructor I tracked the origins of `pat_constructors` returning a `Vec` to commit 9b3f9d94441340f0cdf6ec59aab739baef0f1ac0. It was indeed specifically for variable-length slice patterns. --- src/librustc_mir/hair/pattern/_match.rs | 40 ++++++++++--------------- 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 68486510b764b..527924266fe20 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1615,13 +1615,13 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - if let Some(constructors) = pat_constructors(cx, v.head(), pcx) { - debug!("is_useful - expanding constructors: {:#?}", constructors); + if let Some(constructor) = pat_constructor(cx, v.head(), pcx) { + debug!("is_useful - expanding constructor: {:#?}", constructor); split_grouped_constructors( cx.tcx, cx.param_env, pcx, - constructors, + vec![constructor], matrix, pcx.span, Some(hir_id), @@ -1634,7 +1634,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful - expanding wildcard"); let used_ctors: Vec> = - matrix.heads().flat_map(|p| pat_constructors(cx, p, pcx).unwrap_or(vec![])).collect(); + matrix.heads().filter_map(|p| pat_constructor(cx, p, pcx)).collect(); debug!("used_ctors = {:#?}", used_ctors); // `all_ctors` are all the constructors for the given type, which // should all be represented (or caught with the wild pattern `_`). @@ -1777,47 +1777,39 @@ fn is_useful_specialized<'p, 'a, 'tcx>( .unwrap_or(NotUseful) } -/// Determines the constructors that the given pattern can be specialized to. -/// -/// In most cases, there's only one constructor that a specific pattern -/// represents, such as a specific enum variant or a specific literal value. -/// Slice patterns, however, can match slices of different lengths. For instance, -/// `[a, b, tail @ ..]` can match a slice of length 2, 3, 4 and so on. -/// +/// Determines the constructor that the given pattern can be specialized to. /// Returns `None` in case of a catch-all, which can't be specialized. -fn pat_constructors<'tcx>( +fn pat_constructor<'tcx>( cx: &mut MatchCheckCtxt<'_, 'tcx>, pat: &Pat<'tcx>, pcx: PatCtxt<'tcx>, -) -> Option>> { +) -> Option> { match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => pat_constructors(cx, subpattern, pcx), + PatKind::AscribeUserType { ref subpattern, .. } => pat_constructor(cx, subpattern, pcx), PatKind::Binding { .. } | PatKind::Wild => None, - PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(vec![Single]), + PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(Single), PatKind::Variant { adt_def, variant_index, .. } => { - Some(vec![Variant(adt_def.variants[variant_index].def_id)]) + Some(Variant(adt_def.variants[variant_index].def_id)) } - PatKind::Constant { value } => Some(vec![ConstantValue(value, pat.span)]), - PatKind::Range(PatRange { lo, hi, end }) => Some(vec![ConstantRange( + PatKind::Constant { value } => Some(ConstantValue(value, pat.span)), + PatKind::Range(PatRange { lo, hi, end }) => Some(ConstantRange( lo.eval_bits(cx.tcx, cx.param_env, lo.ty), hi.eval_bits(cx.tcx, cx.param_env, hi.ty), lo.ty, end, pat.span, - )]), + )), PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => { - Some(vec![FixedLenSlice(length.eval_usize(cx.tcx, cx.param_env))]) - } + ty::Array(_, length) => Some(FixedLenSlice(length.eval_usize(cx.tcx, cx.param_env))), _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let prefix = prefix.len() as u64; let suffix = suffix.len() as u64; if slice.is_some() { - Some(vec![VarLenSlice(prefix, suffix)]) + Some(VarLenSlice(prefix, suffix)) } else { - Some(vec![FixedLenSlice(prefix + suffix)]) + Some(FixedLenSlice(prefix + suffix)) } } PatKind::Or { .. } => { From d582ed5684de1951774b637ccfe8b4cf8bd2008b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 5 Nov 2019 16:10:50 +0000 Subject: [PATCH 11/18] Inline max_slice_length Note that where we previously ran `max_slice_len` with input having not only matrix.heads() but also v.head(). Now we run it on matrix.heads() only, but also take into account the currently processed constructor. This preserves behavior since `pat_constructors` returns only one constructor in the case that is of interest for us. --- src/librustc_mir/hair/pattern/_match.rs | 205 ++++++++++++------------ 1 file changed, 99 insertions(+), 106 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 527924266fe20..15f88334e8d5e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -985,7 +985,6 @@ pub enum WitnessPreference { #[derive(Copy, Clone, Debug)] struct PatCtxt<'tcx> { ty: Ty<'tcx>, - max_slice_length: u64, span: Span, } @@ -1143,108 +1142,6 @@ fn all_constructors<'a, 'tcx>( ctors } -fn max_slice_length<'p, 'a, 'tcx, I>(cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 -where - I: Iterator>, - 'tcx: 'p, -{ - // The exhaustiveness-checking paper does not include any details on - // checking variable-length slice patterns. However, they are matched - // by an infinite collection of fixed-length array patterns. - // - // Checking the infinite set directly would take an infinite amount - // of time. However, it turns out that for each finite set of - // patterns `P`, all sufficiently large array lengths are equivalent: - // - // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies - // to exactly the subset `Pₜ` of `P` can be transformed to a slice - // `sₘ` for each sufficiently-large length `m` that applies to exactly - // the same subset of `P`. - // - // Because of that, each witness for reachability-checking from one - // of the sufficiently-large lengths can be transformed to an - // equally-valid witness from any other length, so we only have - // to check slice lengths from the "minimal sufficiently-large length" - // and below. - // - // Note that the fact that there is a *single* `sₘ` for each `m` - // not depending on the specific pattern in `P` is important: if - // you look at the pair of patterns - // `[true, ..]` - // `[.., false]` - // Then any slice of length ≥1 that matches one of these two - // patterns can be trivially turned to a slice of any - // other length ≥1 that matches them and vice-versa - for - // but the slice from length 2 `[false, true]` that matches neither - // of these patterns can't be turned to a slice from length 1 that - // matches neither of these patterns, so we have to consider - // slices from length 2 there. - // - // Now, to see that that length exists and find it, observe that slice - // patterns are either "fixed-length" patterns (`[_, _, _]`) or - // "variable-length" patterns (`[_, .., _]`). - // - // For fixed-length patterns, all slices with lengths *longer* than - // the pattern's length have the same outcome (of not matching), so - // as long as `L` is greater than the pattern's length we can pick - // any `sₘ` from that length and get the same result. - // - // For variable-length patterns, the situation is more complicated, - // because as seen above the precise value of `sₘ` matters. - // - // However, for each variable-length pattern `p` with a prefix of length - // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last - // `slₚ` elements are examined. - // - // Therefore, as long as `L` is positive (to avoid concerns about empty - // types), all elements after the maximum prefix length and before - // the maximum suffix length are not examined by any variable-length - // pattern, and therefore can be added/removed without affecting - // them - creating equivalent patterns from any sufficiently-large - // length. - // - // Of course, if fixed-length patterns exist, we must be sure - // that our length is large enough to miss them all, so - // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})` - // - // for example, with the above pair of patterns, all elements - // but the first and last can be added/removed, so any - // witness of length ≥2 (say, `[false, false, true]`) can be - // turned to a witness from any other length ≥2. - - let mut max_prefix_len = 0; - let mut max_suffix_len = 0; - let mut max_fixed_len = 0; - - for row in patterns { - match *row.kind { - PatKind::Constant { value } => { - // extract the length of an array/slice from a constant - match (value.val, &value.ty.kind) { - (_, ty::Array(_, n)) => { - max_fixed_len = cmp::max(max_fixed_len, n.eval_usize(cx.tcx, cx.param_env)) - } - (ConstValue::Slice { start, end, .. }, ty::Slice(_)) => { - max_fixed_len = cmp::max(max_fixed_len, (end - start) as u64) - } - _ => {} - } - } - PatKind::Slice { ref prefix, slice: None, ref suffix } => { - let fixed_len = prefix.len() as u64 + suffix.len() as u64; - max_fixed_len = cmp::max(max_fixed_len, fixed_len); - } - PatKind::Slice { ref prefix, slice: Some(_), ref suffix } => { - max_prefix_len = cmp::max(max_prefix_len, prefix.len() as u64); - max_suffix_len = cmp::max(max_suffix_len, suffix.len() as u64); - } - _ => {} - } - } - - cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len) -} - /// An inclusive interval, used for precise integer exhaustiveness checking. /// `IntRange`s always store a contiguous range. This means that values are /// encoded such that `0` encodes the minimum value for the integer, @@ -1609,7 +1506,6 @@ pub fn is_useful<'p, 'a, 'tcx>( // introducing uninhabited patterns for inaccessible fields. We // need to figure out how to model that. ty, - max_slice_length: max_slice_length(cx, matrix.heads().chain(Some(v.head()))), span, }; @@ -2088,8 +1984,105 @@ fn split_grouped_constructors<'p, 'tcx>( split_ctors.push(IntRange::range_to_ctor(tcx, ty, range, span)); } } - VarLenSlice(prefix, suffix) => { - split_ctors.extend((prefix + suffix..pcx.max_slice_length + 1).map(FixedLenSlice)) + VarLenSlice(self_prefix, self_suffix) => { + // The exhaustiveness-checking paper does not include any details on + // checking variable-length slice patterns. However, they are matched + // by an infinite collection of fixed-length array patterns. + // + // Checking the infinite set directly would take an infinite amount + // of time. However, it turns out that for each finite set of + // patterns `P`, all sufficiently large array lengths are equivalent: + // + // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies + // to exactly the subset `Pₜ` of `P` can be transformed to a slice + // `sₘ` for each sufficiently-large length `m` that applies to exactly + // the same subset of `P`. + // + // Because of that, each witness for reachability-checking from one + // of the sufficiently-large lengths can be transformed to an + // equally-valid witness from any other length, so we only have + // to check slice lengths from the "minimal sufficiently-large length" + // and below. + // + // Note that the fact that there is a *single* `sₘ` for each `m` + // not depending on the specific pattern in `P` is important: if + // you look at the pair of patterns + // `[true, ..]` + // `[.., false]` + // Then any slice of length ≥1 that matches one of these two + // patterns can be trivially turned to a slice of any + // other length ≥1 that matches them and vice-versa - for + // but the slice from length 2 `[false, true]` that matches neither + // of these patterns can't be turned to a slice from length 1 that + // matches neither of these patterns, so we have to consider + // slices from length 2 there. + // + // Now, to see that that length exists and find it, observe that slice + // patterns are either "fixed-length" patterns (`[_, _, _]`) or + // "variable-length" patterns (`[_, .., _]`). + // + // For fixed-length patterns, all slices with lengths *longer* than + // the pattern's length have the same outcome (of not matching), so + // as long as `L` is greater than the pattern's length we can pick + // any `sₘ` from that length and get the same result. + // + // For variable-length patterns, the situation is more complicated, + // because as seen above the precise value of `sₘ` matters. + // + // However, for each variable-length pattern `p` with a prefix of length + // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last + // `slₚ` elements are examined. + // + // Therefore, as long as `L` is positive (to avoid concerns about empty + // types), all elements after the maximum prefix length and before + // the maximum suffix length are not examined by any variable-length + // pattern, and therefore can be added/removed without affecting + // them - creating equivalent patterns from any sufficiently-large + // length. + // + // Of course, if fixed-length patterns exist, we must be sure + // that our length is large enough to miss them all, so + // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})` + // + // for example, with the above pair of patterns, all elements + // but the first and last can be added/removed, so any + // witness of length ≥2 (say, `[false, false, true]`) can be + // turned to a witness from any other length ≥2. + + let mut max_prefix_len = self_prefix; + let mut max_suffix_len = self_suffix; + let mut max_fixed_len = 0; + + for row in matrix.heads() { + match *row.kind { + PatKind::Constant { value } => { + // extract the length of an array/slice from a constant + match (value.val, &value.ty.kind) { + (_, ty::Array(_, n)) => { + max_fixed_len = + cmp::max(max_fixed_len, n.eval_usize(tcx, param_env)) + } + (ConstValue::Slice { start, end, .. }, ty::Slice(_)) => { + max_fixed_len = cmp::max(max_fixed_len, (end - start) as u64) + } + _ => {} + } + } + PatKind::Slice { ref prefix, slice: None, ref suffix } => { + let fixed_len = prefix.len() as u64 + suffix.len() as u64; + max_fixed_len = cmp::max(max_fixed_len, fixed_len); + } + PatKind::Slice { ref prefix, slice: Some(_), ref suffix } => { + max_prefix_len = cmp::max(max_prefix_len, prefix.len() as u64); + max_suffix_len = cmp::max(max_suffix_len, suffix.len() as u64); + } + _ => {} + } + } + + let max_slice_length = cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len); + split_ctors + .extend((self_prefix + self_suffix..=max_slice_length).map(FixedLenSlice)) } // Any other constructor can be used unchanged. _ => split_ctors.push(ctor), From f774eb69fd19cabaa17dfe91dd0a1b23a2dce842 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 5 Nov 2019 16:30:04 +0000 Subject: [PATCH 12/18] Use VarLenSlice consistently when splitting constructors The previous behaviour ignored slice lengths above a certain length because it could not do otherwise. We now have VarLenSlice however, that can represent the ignored lengths to make the algorithm more consistent. This does not change the correctness of the algorithm, but makes it easier to reason about. As a nice side-effect, exhaustiveness errors have improved: they now capture all missing lengths instead of only the shortest. --- src/librustc_mir/hair/pattern/_match.rs | 23 ++++++++++++++---- .../usefulness/match-slice-patterns.rs | 2 +- .../usefulness/match-slice-patterns.stderr | 4 ++-- .../ui/pattern/usefulness/slice-patterns.rs | 12 +++++----- .../pattern/usefulness/slice-patterns.stderr | 24 +++++++++---------- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 15f88334e8d5e..8d7e605f7c9aa 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -2042,7 +2042,7 @@ fn split_grouped_constructors<'p, 'tcx>( // // Of course, if fixed-length patterns exist, we must be sure // that our length is large enough to miss them all, so - // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})` + // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` // // for example, with the above pair of patterns, all elements // but the first and last can be added/removed, so any @@ -2080,9 +2080,24 @@ fn split_grouped_constructors<'p, 'tcx>( } } - let max_slice_length = cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len); - split_ctors - .extend((self_prefix + self_suffix..=max_slice_length).map(FixedLenSlice)) + // For diagnostics, we keep the prefix and suffix lengths separate, so in the case + // where `max_fixed_len+1` is the largest, we adapt `max_prefix_len` accordingly, + // so that `L = max_prefix_len + max_suffix_len`. + if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { + // The subtraction can't overflow thanks to the above check. + // The new `max_prefix_len` is also guaranteed to be larger than its previous + // value. + max_prefix_len = max_fixed_len + 1 - max_suffix_len; + } + + // `ctor` originally covered the range `(self_prefix + self_suffix..infinity)`. We + // now split it into two: lengths smaller than `max_prefix_len + max_suffix_len` + // are treated independently as fixed-lengths slices, and lengths above are + // captured by a final VarLenSlice constructor. + split_ctors.extend( + (self_prefix + self_suffix..max_prefix_len + max_suffix_len).map(FixedLenSlice), + ); + split_ctors.push(VarLenSlice(max_prefix_len, max_suffix_len)); } // Any other constructor can be used unchanged. _ => split_ctors.push(ctor), diff --git a/src/test/ui/pattern/usefulness/match-slice-patterns.rs b/src/test/ui/pattern/usefulness/match-slice-patterns.rs index afbeb61e4415a..af7fd53a1f1e9 100644 --- a/src/test/ui/pattern/usefulness/match-slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/match-slice-patterns.rs @@ -2,7 +2,7 @@ fn check(list: &[Option<()>]) { match list { - //~^ ERROR `&[_, Some(_), None, _]` not covered + //~^ ERROR `&[_, Some(_), .., None, _]` not covered &[] => {}, &[_] => {}, &[_, _] => {}, diff --git a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr index 24769db34c932..72ae5d5fe3b33 100644 --- a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: `&[_, Some(_), None, _]` not covered +error[E0004]: non-exhaustive patterns: `&[_, Some(_), .., None, _]` not covered --> $DIR/match-slice-patterns.rs:4:11 | LL | match list { - | ^^^^ pattern `&[_, Some(_), None, _]` not covered + | ^^^^ pattern `&[_, Some(_), .., None, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs index e11f11ba7524d..da2d40caf1a40 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -37,7 +37,7 @@ fn main() { [.., false] => {} } match s { - //~^ ERROR `&[false, true]` not covered + //~^ ERROR `&[false, .., true]` not covered [] => {} [true, ..] => {} [.., false] => {} @@ -57,18 +57,18 @@ fn main() { [_] => {} } match s { - //~^ ERROR `&[false]` not covered + //~^ ERROR `&[false, ..]` not covered [] => {} [true, ..] => {} } match s { - //~^ ERROR `&[false, _]` not covered + //~^ ERROR `&[false, _, ..]` not covered [] => {} [_] => {} [true, ..] => {} } match s { - //~^ ERROR `&[_, false]` not covered + //~^ ERROR `&[_, .., false]` not covered [] => {} [_] => {} [.., true] => {} @@ -94,14 +94,14 @@ fn main() { [..] => {} } match s { - //~^ ERROR `&[_, _, true]` not covered + //~^ ERROR `&[_, _, .., true]` not covered [] => {} [_] => {} [_, _] => {} [.., false] => {} } match s { - //~^ ERROR `&[true, _, _]` not covered + //~^ ERROR `&[true, _, .., _]` not covered [] => {} [_] => {} [_, _] => {} diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr index 666e16627c57e..6afe4705b0e69 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -14,11 +14,11 @@ LL | match s3 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[false, true]` not covered +error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered --> $DIR/slice-patterns.rs:39:11 | LL | match s { - | ^ pattern `&[false, true]` not covered + | ^ pattern `&[false, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms @@ -46,27 +46,27 @@ LL | match s { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[false]` not covered +error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered --> $DIR/slice-patterns.rs:59:11 | LL | match s { - | ^ pattern `&[false]` not covered + | ^ pattern `&[false, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[false, _]` not covered +error[E0004]: non-exhaustive patterns: `&[false, _, ..]` not covered --> $DIR/slice-patterns.rs:64:11 | LL | match s { - | ^ pattern `&[false, _]` not covered + | ^ pattern `&[false, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_, false]` not covered +error[E0004]: non-exhaustive patterns: `&[_, .., false]` not covered --> $DIR/slice-patterns.rs:70:11 | LL | match s { - | ^ pattern `&[_, false]` not covered + | ^ pattern `&[_, .., false]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms @@ -112,19 +112,19 @@ error: unreachable pattern LL | [false, true] => {} | ^^^^^^^^^^^^^ -error[E0004]: non-exhaustive patterns: `&[_, _, true]` not covered +error[E0004]: non-exhaustive patterns: `&[_, _, .., true]` not covered --> $DIR/slice-patterns.rs:96:11 | LL | match s { - | ^ pattern `&[_, _, true]` not covered + | ^ pattern `&[_, _, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[true, _, _]` not covered +error[E0004]: non-exhaustive patterns: `&[true, _, .., _]` not covered --> $DIR/slice-patterns.rs:103:11 | LL | match s { - | ^ pattern `&[true, _, _]` not covered + | ^ pattern `&[true, _, .., _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 9531787ae5e8e6a4bfe629b9065233c87b61a654 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 5 Nov 2019 18:40:55 +0000 Subject: [PATCH 13/18] Move a few more tests to the usefulness/ folder --- src/test/ui/pattern/{ => usefulness}/slice-pattern-const-2.rs | 0 src/test/ui/pattern/{ => usefulness}/slice-pattern-const-2.stderr | 0 src/test/ui/pattern/{ => usefulness}/slice-pattern-const-3.rs | 0 src/test/ui/pattern/{ => usefulness}/slice-pattern-const-3.stderr | 0 src/test/ui/pattern/{ => usefulness}/slice-pattern-const.rs | 0 src/test/ui/pattern/{ => usefulness}/slice-pattern-const.stderr | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-const-2.rs (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-const-2.stderr (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-const-3.rs (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-const-3.stderr (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-const.rs (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-const.stderr (100%) diff --git a/src/test/ui/pattern/slice-pattern-const-2.rs b/src/test/ui/pattern/usefulness/slice-pattern-const-2.rs similarity index 100% rename from src/test/ui/pattern/slice-pattern-const-2.rs rename to src/test/ui/pattern/usefulness/slice-pattern-const-2.rs diff --git a/src/test/ui/pattern/slice-pattern-const-2.stderr b/src/test/ui/pattern/usefulness/slice-pattern-const-2.stderr similarity index 100% rename from src/test/ui/pattern/slice-pattern-const-2.stderr rename to src/test/ui/pattern/usefulness/slice-pattern-const-2.stderr diff --git a/src/test/ui/pattern/slice-pattern-const-3.rs b/src/test/ui/pattern/usefulness/slice-pattern-const-3.rs similarity index 100% rename from src/test/ui/pattern/slice-pattern-const-3.rs rename to src/test/ui/pattern/usefulness/slice-pattern-const-3.rs diff --git a/src/test/ui/pattern/slice-pattern-const-3.stderr b/src/test/ui/pattern/usefulness/slice-pattern-const-3.stderr similarity index 100% rename from src/test/ui/pattern/slice-pattern-const-3.stderr rename to src/test/ui/pattern/usefulness/slice-pattern-const-3.stderr diff --git a/src/test/ui/pattern/slice-pattern-const.rs b/src/test/ui/pattern/usefulness/slice-pattern-const.rs similarity index 100% rename from src/test/ui/pattern/slice-pattern-const.rs rename to src/test/ui/pattern/usefulness/slice-pattern-const.rs diff --git a/src/test/ui/pattern/slice-pattern-const.stderr b/src/test/ui/pattern/usefulness/slice-pattern-const.stderr similarity index 100% rename from src/test/ui/pattern/slice-pattern-const.stderr rename to src/test/ui/pattern/usefulness/slice-pattern-const.stderr From 816aee2501812c9e910c962c052a7166a37aa289 Mon Sep 17 00:00:00 2001 From: Nadrieril Feneanar Date: Wed, 6 Nov 2019 14:51:24 +0000 Subject: [PATCH 14/18] Apply suggestions from code review Co-Authored-By: Mazdak Farrokhzad --- src/librustc_mir/hair/pattern/_match.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8d7e605f7c9aa..47f8c699e396e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -586,9 +586,9 @@ enum Constructor<'tcx> { ConstantValue(&'tcx ty::Const<'tcx>, Span), /// Ranges of literal values (`2..=5` and `2..5`). ConstantRange(u128, u128, Ty<'tcx>, RangeEnd, Span), - /// Array patterns of length n. + /// Array patterns of length `n`. FixedLenSlice(u64), - /// Slice patterns. Captures any array constructor of length >= i+j. + /// Slice patterns. Captures any array constructor of `length >= i + j`. VarLenSlice(u64, u64), } @@ -616,8 +616,7 @@ impl<'tcx> std::cmp::PartialEq for Constructor<'tcx> { impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { - FixedLenSlice { .. } => true, - VarLenSlice { .. } => true, + FixedLenSlice { .. } | VarLenSlice { .. } => true, _ => false, } } @@ -687,13 +686,13 @@ impl<'tcx> Constructor<'tcx> { // For each used ctor, subtract from the current set of constructors. // Naming: we remove the "neg" constructors from the "pos" ones. - // Remember, VarLenSlice(i, j) covers the union of FixedLenSlice from - // i+j to infinity. + // Remember, `VarLenSlice(i, j)` covers the union of `FixedLenSlice` from + // `i + j` to infinity. for neg_ctor in other_ctors { remaining_ctors = remaining_ctors .into_iter() .flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> { - // Compute pos_ctor \ neg_ctor + // Compute `pos_ctor \ neg_ctor`. match (&pos_ctor, neg_ctor) { (&FixedLenSlice(pos_len), &VarLenSlice(neg_prefix, neg_suffix)) => { let neg_len = neg_prefix + neg_suffix; @@ -722,7 +721,7 @@ impl<'tcx> Constructor<'tcx> { } else { (pos_len..neg_len) .map(FixedLenSlice) - // We know neg_len + 1 >= pos_len >= pos_suffix + // We know that `neg_len + 1 >= pos_len >= pos_suffix`. .chain(Some(VarLenSlice( neg_len + 1 - pos_suffix, pos_suffix, @@ -2081,7 +2080,7 @@ fn split_grouped_constructors<'p, 'tcx>( } // For diagnostics, we keep the prefix and suffix lengths separate, so in the case - // where `max_fixed_len+1` is the largest, we adapt `max_prefix_len` accordingly, + // where `max_fixed_len + 1` is the largest, we adapt `max_prefix_len` accordingly, // so that `L = max_prefix_len + max_suffix_len`. if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { // The subtraction can't overflow thanks to the above check. From 7514c48917b9a971dd9e21837edf9e80ff40c811 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 6 Nov 2019 15:06:57 +0000 Subject: [PATCH 15/18] Factor out constructing a new wildcard pattern --- src/librustc_mir/hair/pattern/_match.rs | 10 +++------- src/librustc_mir/hair/pattern/check_match.rs | 4 ++-- src/librustc_mir/hair/pattern/mod.rs | 7 ++++++- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 47f8c699e396e..495c6de0f5ae9 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -780,11 +780,7 @@ impl<'tcx> Constructor<'tcx> { cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>, ) -> impl Iterator> + DoubleEndedIterator { - constructor_sub_pattern_tys(cx, self, ty).into_iter().map(|ty| Pat { - ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - }) + constructor_sub_pattern_tys(cx, self, ty).into_iter().map(Pat::wildcard_from_ty) } /// This computes the arity of a constructor. The arity of a constructor @@ -862,7 +858,7 @@ impl<'tcx> Constructor<'tcx> { VarLenSlice(prefix_len, _suffix_len) => { let prefix = subpatterns.by_ref().take(*prefix_len as usize).collect(); let suffix = subpatterns.collect(); - let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; + let wild = Pat::wildcard_from_ty(ty); PatKind::Slice { prefix, slice: Some(wild), suffix } } _ => bug!("bad slice pattern {:?} {:?}", self, ty), @@ -931,7 +927,7 @@ impl<'tcx> Usefulness<'tcx> { fn apply_wildcard(self, ty: Ty<'tcx>) -> Self { match self { UsefulWithWitness(witnesses) => { - let wild = Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; + let wild = Pat::wildcard_from_ty(ty); UsefulWithWitness( witnesses .into_iter() diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 9d370554e86b4..a63a9a687d8d3 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -18,7 +18,7 @@ use rustc::hir::{self, Pat}; use std::slice; -use syntax_pos::{MultiSpan, Span, DUMMY_SP}; +use syntax_pos::{MultiSpan, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match tcx.hir().as_local_hir_id(def_id) { @@ -491,7 +491,7 @@ fn check_not_useful( matrix: &Matrix<'_, 'tcx>, hir_id: HirId, ) -> Result<(), Vec>> { - let wild_pattern = super::Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; + let wild_pattern = super::Pat::wildcard_from_ty(ty); match is_useful(cx, matrix, &PatStack::from_pattern(&wild_pattern), ConstructWitness, hir_id) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 1ecc78ba227ce..9e9f5f8fa9094 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -26,7 +26,7 @@ use rustc_index::vec::Idx; use std::cmp::Ordering; use std::fmt; use syntax::ast; -use syntax_pos::Span; +use syntax_pos::{Span, DUMMY_SP}; #[derive(Clone, Debug)] pub enum PatternError { @@ -55,6 +55,11 @@ pub struct Pat<'tcx> { pub kind: Box>, } +impl<'tcx> Pat<'tcx> { + pub(crate) fn wildcard_from_ty(ty: Ty<'tcx>) -> Self { + Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) } + } +} #[derive(Copy, Clone, Debug, PartialEq)] pub struct PatTyProj<'tcx> { From 098974dadd73902d6169e36eb1f57f48ce926b32 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 6 Nov 2019 15:27:21 +0000 Subject: [PATCH 16/18] Split slice-patterns test as suggested by Centril --- ...ns.rs => slice-patterns-exhaustiveness.rs} | 35 ---------- ...r => slice-patterns-exhaustiveness.stderr} | 66 ++++--------------- .../usefulness/slice-patterns-irrefutable.rs | 27 ++++++++ .../usefulness/slice-patterns-reachability.rs | 26 ++++++++ .../slice-patterns-reachability.stderr | 44 +++++++++++++ 5 files changed, 109 insertions(+), 89 deletions(-) rename src/test/ui/pattern/usefulness/{slice-patterns.rs => slice-patterns-exhaustiveness.rs} (64%) rename src/test/ui/pattern/usefulness/{slice-patterns.stderr => slice-patterns-exhaustiveness.stderr} (69%) create mode 100644 src/test/ui/pattern/usefulness/slice-patterns-irrefutable.rs create mode 100644 src/test/ui/pattern/usefulness/slice-patterns-reachability.rs create mode 100644 src/test/ui/pattern/usefulness/slice-patterns-reachability.stderr diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs similarity index 64% rename from src/test/ui/pattern/usefulness/slice-patterns.rs rename to src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs index da2d40caf1a40..86cdf160618bf 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.rs @@ -1,27 +1,11 @@ #![feature(slice_patterns)] -#![deny(unreachable_patterns)] fn main() { let s: &[bool] = &[true; 0]; - let s0: &[bool; 0] = &[]; let s1: &[bool; 1] = &[false; 1]; let s2: &[bool; 2] = &[false; 2]; let s3: &[bool; 3] = &[false; 3]; - let [] = s0; - let [_] = s1; - let [_, _] = s2; - - let [..] = s; - let [..] = s0; - let [..] = s1; - let [..] = s2; - let [..] = s3; - - let [_, _, ..] = s2; - let [_, .., _] = s2; - let [.., _, _] = s2; - match s1 { [true, ..] => {} [.., false] => {} @@ -74,25 +58,6 @@ fn main() { [.., true] => {} } - match s { - [true, ..] => {} - [true, ..] => {} //~ ERROR unreachable pattern - [true] => {} //~ ERROR unreachable pattern - [..] => {} - } - match s { - [.., true] => {} - [.., true] => {} //~ ERROR unreachable pattern - [true] => {} //~ ERROR unreachable pattern - [..] => {} - } - match s { - [false, .., true] => {} - [false, .., true] => {} //~ ERROR unreachable pattern - [false, true] => {} //~ ERROR unreachable pattern - [false] => {} - [..] => {} - } match s { //~^ ERROR `&[_, _, .., true]` not covered [] => {} diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr similarity index 69% rename from src/test/ui/pattern/usefulness/slice-patterns.stderr rename to src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr index 6afe4705b0e69..1391b520556dc 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns-exhaustiveness.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `&[false, true]` not covered - --> $DIR/slice-patterns.rs:29:11 + --> $DIR/slice-patterns-exhaustiveness.rs:13:11 | LL | match s2 { | ^^ pattern `&[false, true]` not covered @@ -7,7 +7,7 @@ LL | match s2 { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[false, _, true]` not covered - --> $DIR/slice-patterns.rs:34:11 + --> $DIR/slice-patterns-exhaustiveness.rs:18:11 | LL | match s3 { | ^^ pattern `&[false, _, true]` not covered @@ -15,7 +15,7 @@ LL | match s3 { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered - --> $DIR/slice-patterns.rs:39:11 + --> $DIR/slice-patterns-exhaustiveness.rs:23:11 | LL | match s { | ^ pattern `&[false, .., true]` not covered @@ -23,7 +23,7 @@ LL | match s { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[false, _, _]` not covered - --> $DIR/slice-patterns.rs:46:11 + --> $DIR/slice-patterns-exhaustiveness.rs:30:11 | LL | match s3 { | ^^ pattern `&[false, _, _]` not covered @@ -31,7 +31,7 @@ LL | match s3 { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/slice-patterns.rs:50:11 + --> $DIR/slice-patterns-exhaustiveness.rs:34:11 | LL | match s { | ^ pattern `&[_, ..]` not covered @@ -39,7 +39,7 @@ LL | match s { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered - --> $DIR/slice-patterns.rs:54:11 + --> $DIR/slice-patterns-exhaustiveness.rs:38:11 | LL | match s { | ^ pattern `&[_, _, ..]` not covered @@ -47,7 +47,7 @@ LL | match s { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered - --> $DIR/slice-patterns.rs:59:11 + --> $DIR/slice-patterns-exhaustiveness.rs:43:11 | LL | match s { | ^ pattern `&[false, ..]` not covered @@ -55,7 +55,7 @@ LL | match s { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[false, _, ..]` not covered - --> $DIR/slice-patterns.rs:64:11 + --> $DIR/slice-patterns-exhaustiveness.rs:48:11 | LL | match s { | ^ pattern `&[false, _, ..]` not covered @@ -63,57 +63,15 @@ LL | match s { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[_, .., false]` not covered - --> $DIR/slice-patterns.rs:70:11 + --> $DIR/slice-patterns-exhaustiveness.rs:54:11 | LL | match s { | ^ pattern `&[_, .., false]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: unreachable pattern - --> $DIR/slice-patterns.rs:79:9 - | -LL | [true, ..] => {} - | ^^^^^^^^^^ - | -note: lint level defined here - --> $DIR/slice-patterns.rs:2:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/slice-patterns.rs:80:9 - | -LL | [true] => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/slice-patterns.rs:85:9 - | -LL | [.., true] => {} - | ^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/slice-patterns.rs:86:9 - | -LL | [true] => {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/slice-patterns.rs:91:9 - | -LL | [false, .., true] => {} - | ^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/slice-patterns.rs:92:9 - | -LL | [false, true] => {} - | ^^^^^^^^^^^^^ - error[E0004]: non-exhaustive patterns: `&[_, _, .., true]` not covered - --> $DIR/slice-patterns.rs:96:11 + --> $DIR/slice-patterns-exhaustiveness.rs:61:11 | LL | match s { | ^ pattern `&[_, _, .., true]` not covered @@ -121,13 +79,13 @@ LL | match s { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `&[true, _, .., _]` not covered - --> $DIR/slice-patterns.rs:103:11 + --> $DIR/slice-patterns-exhaustiveness.rs:68:11 | LL | match s { | ^ pattern `&[true, _, .., _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to 17 previous errors +error: aborting due to 11 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/slice-patterns-irrefutable.rs b/src/test/ui/pattern/usefulness/slice-patterns-irrefutable.rs new file mode 100644 index 0000000000000..3b716bae77209 --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns-irrefutable.rs @@ -0,0 +1,27 @@ +// check-pass +#![feature(slice_patterns)] + +fn main() { + let s: &[bool] = &[true; 0]; + let s0: &[bool; 0] = &[]; + let s1: &[bool; 1] = &[false; 1]; + let s2: &[bool; 2] = &[false; 2]; + + let [] = s0; + let [_] = s1; + let [_, _] = s2; + + let [..] = s; + let [..] = s0; + let [..] = s1; + let [..] = s2; + + let [_, ..] = s1; + let [.., _] = s1; + let [_, ..] = s2; + let [.., _] = s2; + + let [_, _, ..] = s2; + let [_, .., _] = s2; + let [.., _, _] = s2; +} diff --git a/src/test/ui/pattern/usefulness/slice-patterns-reachability.rs b/src/test/ui/pattern/usefulness/slice-patterns-reachability.rs new file mode 100644 index 0000000000000..35d9dc91aee1c --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns-reachability.rs @@ -0,0 +1,26 @@ +#![feature(slice_patterns)] +#![deny(unreachable_patterns)] + +fn main() { + let s: &[bool] = &[true; 0]; + + match s { + [true, ..] => {} + [true, ..] => {} //~ ERROR unreachable pattern + [true] => {} //~ ERROR unreachable pattern + [..] => {} + } + match s { + [.., true] => {} + [.., true] => {} //~ ERROR unreachable pattern + [true] => {} //~ ERROR unreachable pattern + [..] => {} + } + match s { + [false, .., true] => {} + [false, .., true] => {} //~ ERROR unreachable pattern + [false, true] => {} //~ ERROR unreachable pattern + [false] => {} + [..] => {} + } +} diff --git a/src/test/ui/pattern/usefulness/slice-patterns-reachability.stderr b/src/test/ui/pattern/usefulness/slice-patterns-reachability.stderr new file mode 100644 index 0000000000000..333ce170283ea --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns-reachability.stderr @@ -0,0 +1,44 @@ +error: unreachable pattern + --> $DIR/slice-patterns-reachability.rs:9:9 + | +LL | [true, ..] => {} + | ^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/slice-patterns-reachability.rs:2:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns-reachability.rs:10:9 + | +LL | [true] => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns-reachability.rs:15:9 + | +LL | [.., true] => {} + | ^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns-reachability.rs:16:9 + | +LL | [true] => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns-reachability.rs:21:9 + | +LL | [false, .., true] => {} + | ^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns-reachability.rs:22:9 + | +LL | [false, true] => {} + | ^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + From 369a351010240b45da36c314c1363aca76b2ded0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 7 Nov 2019 12:04:19 +0000 Subject: [PATCH 17/18] Inline constructor_sub_pattern_tys It was only used at a single location --- src/librustc_mir/hair/pattern/_match.rs | 130 ++++++++++++------------ 1 file changed, 63 insertions(+), 67 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 495c6de0f5ae9..a494a5a95b752 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -779,8 +779,67 @@ impl<'tcx> Constructor<'tcx> { &self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>, - ) -> impl Iterator> + DoubleEndedIterator { - constructor_sub_pattern_tys(cx, self, ty).into_iter().map(Pat::wildcard_from_ty) + ) -> Vec> { + debug!("wildcard_subpatterns({:#?}, {:?})", self, ty); + match ty.kind { + ty::Tuple(ref fs) => { + fs.into_iter().map(|t| t.expect_ty()).map(Pat::wildcard_from_ty).collect() + } + ty::Slice(ty) | ty::Array(ty, _) => match *self { + FixedLenSlice(length) => (0..length).map(|_| Pat::wildcard_from_ty(ty)).collect(), + VarLenSlice(prefix, suffix) => { + (0..prefix + suffix).map(|_| Pat::wildcard_from_ty(ty)).collect() + } + ConstantValue(..) => vec![], + _ => bug!("bad slice pattern {:?} {:?}", self, ty), + }, + ty::Ref(_, rty, _) => vec![Pat::wildcard_from_ty(rty)], + ty::Adt(adt, substs) => { + if adt.is_box() { + // Use T as the sub pattern type of Box. + vec![Pat::wildcard_from_ty(substs.type_at(0))] + } else { + let variant = &adt.variants[self.variant_index_for_adt(cx, adt)]; + let is_non_exhaustive = + variant.is_field_list_non_exhaustive() && !cx.is_local(ty); + variant + .fields + .iter() + .map(|field| { + let is_visible = + adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); + let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs)); + match (is_visible, is_non_exhaustive, is_uninhabited) { + // Treat all uninhabited types in non-exhaustive variants as `TyErr`. + (_, true, true) => cx.tcx.types.err, + // Treat all non-visible fields as `TyErr`. They can't appear in any + // other pattern from this match (because they are private), so their + // type does not matter - but we don't want to know they are + // uninhabited. + (false, ..) => cx.tcx.types.err, + (true, ..) => { + let ty = field.ty(cx.tcx, substs); + match ty.kind { + // If the field type returned is an array of an unknown size + // return an TyErr. + ty::Array(_, len) + if len + .try_eval_usize(cx.tcx, cx.param_env) + .is_none() => + { + cx.tcx.types.err + } + _ => ty, + } + } + } + }) + .map(Pat::wildcard_from_ty) + .collect() + } + } + _ => vec![], + } } /// This computes the arity of a constructor. The arity of a constructor @@ -880,7 +939,7 @@ impl<'tcx> Constructor<'tcx> { /// Like `apply`, but where all the subpatterns are wildcards `_`. fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { - let subpatterns = self.wildcard_subpatterns(cx, ty).rev(); + let subpatterns = self.wildcard_subpatterns(cx, ty).into_iter().rev(); self.apply(cx, ty, subpatterns) } } @@ -1659,7 +1718,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( ) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); - let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect(); + let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty); let ctor_wild_subpatterns: Vec<_> = ctor_wild_subpatterns_owned.iter().collect(); let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) @@ -1709,69 +1768,6 @@ fn pat_constructor<'tcx>( } } -/// This computes the types of the sub patterns that a constructor should be -/// expanded to. -/// -/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char]. -fn constructor_sub_pattern_tys<'a, 'tcx>( - cx: &MatchCheckCtxt<'a, 'tcx>, - ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, -) -> Vec> { - debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty); - match ty.kind { - ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), - ty::Slice(ty) | ty::Array(ty, _) => match *ctor { - FixedLenSlice(length) => (0..length).map(|_| ty).collect(), - VarLenSlice(prefix, suffix) => (0..prefix + suffix).map(|_| ty).collect(), - ConstantValue(..) => vec![], - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), - }, - ty::Ref(_, rty, _) => vec![rty], - ty::Adt(adt, substs) => { - if adt.is_box() { - // Use T as the sub pattern type of Box. - vec![substs.type_at(0)] - } else { - let variant = &adt.variants[ctor.variant_index_for_adt(cx, adt)]; - let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(ty); - variant - .fields - .iter() - .map(|field| { - let is_visible = - adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs)); - match (is_visible, is_non_exhaustive, is_uninhabited) { - // Treat all uninhabited types in non-exhaustive variants as `TyErr`. - (_, true, true) => cx.tcx.types.err, - // Treat all non-visible fields as `TyErr`. They can't appear in any - // other pattern from this match (because they are private), so their - // type does not matter - but we don't want to know they are - // uninhabited. - (false, ..) => cx.tcx.types.err, - (true, ..) => { - let ty = field.ty(cx.tcx, substs); - match ty.kind { - // If the field type returned is an array of an unknown size - // return an TyErr. - ty::Array(_, len) - if len.try_eval_usize(cx.tcx, cx.param_env).is_none() => - { - cx.tcx.types.err - } - _ => ty, - } - } - } - }) - .collect() - } - } - _ => vec![], - } -} - // checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, // meaning all other types will compare unequal and thus equal patterns often do not cause the // second pattern to lint about unreachable match arms. From fd9921b41c4354aaf790c2bc0393f818034100d0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 10 Nov 2019 10:56:06 +0000 Subject: [PATCH 18/18] tidy --- src/librustc_mir/hair/pattern/_match.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index a494a5a95b752..982330baf9c95 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -810,18 +810,19 @@ impl<'tcx> Constructor<'tcx> { adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); let is_uninhabited = cx.is_uninhabited(field.ty(cx.tcx, substs)); match (is_visible, is_non_exhaustive, is_uninhabited) { - // Treat all uninhabited types in non-exhaustive variants as `TyErr`. + // Treat all uninhabited types in non-exhaustive variants as + // `TyErr`. (_, true, true) => cx.tcx.types.err, - // Treat all non-visible fields as `TyErr`. They can't appear in any - // other pattern from this match (because they are private), so their - // type does not matter - but we don't want to know they are + // Treat all non-visible fields as `TyErr`. They can't appear in + // any other pattern from this match (because they are private), so + // their type does not matter - but we don't want to know they are // uninhabited. (false, ..) => cx.tcx.types.err, (true, ..) => { let ty = field.ty(cx.tcx, substs); match ty.kind { - // If the field type returned is an array of an unknown size - // return an TyErr. + // If the field type returned is an array of an unknown + // size return an TyErr. ty::Array(_, len) if len .try_eval_usize(cx.tcx, cx.param_env)