From fe671966796e8bd7e468fcce86893d7a27af08bc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 3 Nov 2019 17:10:33 +0000 Subject: [PATCH 1/8] Don't build the same matrix twice The exact same logic was used in check_arms and check_match to build the matrix of relevant patterns. It would actually probably have been a bug if it was not the case, since exhaustiveness checking should be the same as checking reachability of an additional `_ => ...` match branch. --- src/librustc_mir/hair/pattern/check_match.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 30586e95acf8d..4463fb43583aa 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -181,7 +181,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { } // Fourth, check for unreachable arms. - check_arms(cx, &inlined_arms, source); + let matrix = check_arms(cx, &inlined_arms, source); // Then, if the match has no arms, check whether the scrutinee // is uninhabited. @@ -248,12 +248,6 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { return; } - let matrix: Matrix<'_, '_> = inlined_arms - .iter() - .filter(|&&(_, guard)| guard.is_none()) - .flat_map(|arm| &arm.0) - .map(|pat| PatStack::from_pattern(pat.0)) - .collect(); let scrut_ty = self.tables.node_type(scrut.hir_id); check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id); }) @@ -403,11 +397,11 @@ fn pat_is_catchall(pat: &Pat) -> bool { } // Check for unreachable patterns -fn check_arms<'tcx>( +fn check_arms<'p, 'tcx>( cx: &mut MatchCheckCtxt<'_, 'tcx>, - arms: &[(Vec<(&super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], + arms: &[(Vec<(&'p super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], source: hir::MatchSource, -) { +) -> Matrix<'p, 'tcx> { let mut seen = Matrix::empty(); let mut catchall = None; for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() { @@ -485,6 +479,7 @@ fn check_arms<'tcx>( } } } + seen } fn check_not_useful( From 21af89d773cc76eaf7240e4a16f30d4cd29139e1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 27 Nov 2019 16:50:42 +0000 Subject: [PATCH 2/8] `UsefulWithWitness` always carries some witnesses --- src/librustc_mir/hair/pattern/check_match.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 4463fb43583aa..56f0ace489180 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -492,7 +492,7 @@ fn check_not_useful( 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() { - vec![wild_pattern] + bug!("Exhaustiveness check returned no witnesses") } else { pats.into_iter().map(|w| w.single_pattern()).collect() }), From e6aa96246fe6125dae72d2840749f2859b49a47f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 28 Nov 2019 13:03:02 +0000 Subject: [PATCH 3/8] Simplify lifetimes by allocating patterns on the arena We want the lifetimes of the patterns contained in the matrix and the candidate `PatStack` to be the same so that they can be mixed together. A lot of this would not be necessary if `SmallVec` was covariant in its type argument (see https://github.com/servo/rust-smallvec/issues/146). --- src/librustc_mir/hair/pattern/_match.rs | 65 ++++++++------------ src/librustc_mir/hair/pattern/check_match.rs | 22 +++---- 2 files changed, 38 insertions(+), 49 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f2c5bf1bf6d55..e2ed925047442 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -425,16 +425,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor<'a, 'q>( + fn specialize_constructor( &self, - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &[&'q Pat<'tcx>], - ) -> Option> - where - 'a: 'q, - 'p: 'q, - { + ctor_wild_subpatterns: &'p [Pat<'tcx>], + ) -> Option> { let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); new_heads.map(|mut new_head| { new_head.0.extend_from_slice(&self.0[1..]); @@ -486,16 +482,12 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize_constructor<'a, 'q>( + fn specialize_constructor( &self, - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &mut MatchCheckCtxt<'p, 'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &[&'q Pat<'tcx>], - ) -> Matrix<'q, 'tcx> - where - 'a: 'q, - 'p: 'q, - { + ctor_wild_subpatterns: &'p [Pat<'tcx>], + ) -> Matrix<'p, 'tcx> { self.0 .iter() .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) @@ -1604,10 +1596,10 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { /// relation to preceding patterns, it is not reachable) and exhaustiveness /// checking (if a wildcard pattern is useful in relation to a matrix, the /// matrix isn't exhaustive). -pub fn is_useful<'p, 'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, +pub fn is_useful<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'_, 'tcx>, + v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, hir_id: HirId, ) -> Usefulness<'tcx> { @@ -1768,10 +1760,10 @@ pub fn is_useful<'p, 'a, 'tcx>( /// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied /// to the specialised version of both the pattern matrix `P` and the new pattern `q`. -fn is_useful_specialized<'p, 'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, +fn is_useful_specialized<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, matrix: &Matrix<'p, 'tcx>, - v: &PatStack<'_, 'tcx>, + v: &PatStack<'p, 'tcx>, ctor: Constructor<'tcx>, lty: Ty<'tcx>, witness_preference: WitnessPreference, @@ -1779,10 +1771,10 @@ 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); - 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) + let ctor_wild_subpatterns = + cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty)); + let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns); + v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns) .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id)) .map(|u| u.apply_constructor(cx, &ctor, lty)) .unwrap_or(NotUseful) @@ -2250,13 +2242,13 @@ fn constructor_covered_by_range<'tcx>( if intersects { Some(()) } else { None } } -fn patterns_for_variant<'p, 'a: 'p, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, +fn patterns_for_variant<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, subpatterns: &'p [FieldPat<'tcx>], - ctor_wild_subpatterns: &[&'p Pat<'tcx>], + ctor_wild_subpatterns: &'p [Pat<'tcx>], is_non_exhaustive: bool, ) -> PatStack<'p, 'tcx> { - let mut result = SmallVec::from_slice(ctor_wild_subpatterns); + let mut result: SmallVec<_> = ctor_wild_subpatterns.iter().collect(); for subpat in subpatterns { if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) { @@ -2280,11 +2272,11 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( /// different patterns. /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. -fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, - pat: &'q Pat<'tcx>, +fn specialize_one_pattern<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, + pat: &'p Pat<'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &[&'p Pat<'tcx>], + ctor_wild_subpatterns: &'p [Pat<'tcx>], ) -> Option> { if let NonExhaustive = constructor { // Only a wildcard pattern can match the special extra constructor @@ -2294,9 +2286,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( let result = match *pat.kind { PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern` - PatKind::Binding { .. } | PatKind::Wild => { - Some(PatStack::from_slice(ctor_wild_subpatterns)) - } + PatKind::Binding { .. } | PatKind::Wild => Some(ctor_wild_subpatterns.iter().collect()), PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; @@ -2406,7 +2396,6 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( .chain( ctor_wild_subpatterns .iter() - .map(|p| *p) .skip(prefix.len()) .take(slice_count) .chain(suffix.iter()), diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 56f0ace489180..62bc04b65f34f 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -261,8 +261,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { patcx.include_lint_checks(); let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; - let pattern = expand_pattern(cx, pattern); - let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(&pattern)].into_iter().collect(); + let pattern = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); + let pats: Matrix<'_, '_> = vec![PatStack::from_pattern(pattern)].into_iter().collect(); let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) { Ok(_) => return, @@ -398,7 +398,7 @@ fn pat_is_catchall(pat: &Pat) -> bool { // Check for unreachable patterns fn check_arms<'p, 'tcx>( - cx: &mut MatchCheckCtxt<'_, 'tcx>, + cx: &mut MatchCheckCtxt<'p, 'tcx>, arms: &[(Vec<(&'p super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], source: hir::MatchSource, ) -> Matrix<'p, 'tcx> { @@ -482,14 +482,14 @@ fn check_arms<'p, 'tcx>( seen } -fn check_not_useful( - cx: &mut MatchCheckCtxt<'_, 'tcx>, +fn check_not_useful<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>, - matrix: &Matrix<'_, 'tcx>, + matrix: &Matrix<'p, 'tcx>, hir_id: HirId, ) -> Result<(), Vec>> { - let wild_pattern = super::Pat::wildcard_from_ty(ty); - match is_useful(cx, matrix, &PatStack::from_pattern(&wild_pattern), ConstructWitness, hir_id) { + let wild_pattern = cx.pattern_arena.alloc(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() { bug!("Exhaustiveness check returned no witnesses") @@ -500,11 +500,11 @@ fn check_not_useful( } } -fn check_exhaustive<'tcx>( - cx: &mut MatchCheckCtxt<'_, 'tcx>, +fn check_exhaustive<'p, 'tcx>( + cx: &mut MatchCheckCtxt<'p, 'tcx>, scrut_ty: Ty<'tcx>, sp: Span, - matrix: &Matrix<'_, 'tcx>, + matrix: &Matrix<'p, 'tcx>, hir_id: HirId, ) { let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { From 00ccadf43f02c1991e352a1eb3e0880ada52cada Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Nov 2019 12:25:36 +0000 Subject: [PATCH 4/8] Add some tests --- .../ui/or-patterns/exhaustiveness-pass.rs | 46 ++++++++--- .../usefulness/top-level-alternation.rs | 58 ++++++++++++++ .../usefulness/top-level-alternation.stderr | 76 +++++++++++++++++++ 3 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/pattern/usefulness/top-level-alternation.rs create mode 100644 src/test/ui/pattern/usefulness/top-level-alternation.stderr diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index 62a851719f96d..d49ae74db51b5 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -6,35 +6,63 @@ // We wrap patterns in a tuple because top-level or-patterns are special-cased for now. fn main() { // Get the fatal error out of the way - match (0u8,) { + match (0,) { (0 | _,) => {} //~^ ERROR or-patterns are not fully implemented yet } - match (0u8,) { + match (0,) { (1 | 2,) => {} _ => {} } - match (0u8,) { - (1 | 1,) => {} // FIXME(or_patterns): redundancy not detected for now. - _ => {} - } - match (0u8, 0u8) { + match (0, 0) { (1 | 2, 3 | 4) => {} (1, 2) => {} - (2, 1) => {} + (3, 1) => {} _ => {} } match (Some(0u8),) { (None | Some(0 | 1),) => {} (Some(2..=255),) => {} } - match ((0u8,),) { + match ((0,),) { ((0 | 1,) | (2 | 3,),) => {}, ((_,),) => {}, } match (&[0u8][..],) { ([] | [0 | 1..=255] | [_, ..],) => {}, } + + match ((0, 0),) { + ((0, 0) | (0, 1),) => {} + _ => {} + } + match ((0, 0),) { + ((0, 0) | (1, 0),) => {} + _ => {} + } + + // FIXME(or_patterns): Redundancies not detected for now. + match (0,) { + (1 | 1,) => {} + _ => {} + } + match [0; 2] { + [0 | 0, 0 | 0] => {} + _ => {} + } + match &[][..] { + [0] => {} + [0, _] => {} + [0, _, _] => {} + [1, ..] => {} + [1 | 2, ..] => {} + _ => {} + } + match Some(0) { + Some(0) => {} + Some(0 | 1) => {} + _ => {} + } } diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.rs b/src/test/ui/pattern/usefulness/top-level-alternation.rs new file mode 100644 index 0000000000000..6ba9b45847797 --- /dev/null +++ b/src/test/ui/pattern/usefulness/top-level-alternation.rs @@ -0,0 +1,58 @@ +#![deny(unreachable_patterns)] + +fn main() { + while let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern + if let 0..=2 | 1 = 0 {} //~ WARN irrefutable if-let pattern + // this one ^ is incorrect + + match 0u8 { + 0 + | 0 => {} //~ ERROR unreachable pattern + _ => {} + } + match Some(0u8) { + Some(0) + | Some(0) => {} //~ ERROR unreachable pattern + _ => {} + } + match (0u8, 0u8) { + (0, _) | (_, 0) => {} + (0, 0) => {} //~ ERROR unreachable pattern + (1, 1) => {} + _ => {} + } + match (0u8, 0u8) { + (0, 1) | (2, 3) => {} + (0, 3) => {} + (2, 1) => {} + _ => {} + } + match (0u8, 0u8) { + (_, 0) | (_, 1) => {} + _ => {} + } + match (0u8, 0u8) { + (0, _) | (1, _) => {} + _ => {} + } + match Some(0u8) { + None | Some(_) => {} + _ => {} //~ ERROR unreachable pattern + } + match Some(0u8) { + None | Some(_) => {} + Some(_) => {} //~ ERROR unreachable pattern + None => {} //~ ERROR unreachable pattern + } + match Some(0u8) { + Some(_) => {} + None => {} + None //~ ERROR unreachable pattern + | Some(_) => {} //~ ERROR unreachable pattern + } + match 0u8 { + 1 | 2 => {}, + 1..=2 => {}, //~ ERROR unreachable pattern + _ => {}, + } +} diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.stderr b/src/test/ui/pattern/usefulness/top-level-alternation.stderr new file mode 100644 index 0000000000000..ef7327471212a --- /dev/null +++ b/src/test/ui/pattern/usefulness/top-level-alternation.stderr @@ -0,0 +1,76 @@ +error: unreachable pattern + --> $DIR/top-level-alternation.rs:4:23 + | +LL | while let 0..=2 | 1 = 0 {} + | ^ + | +note: lint level defined here + --> $DIR/top-level-alternation.rs:1:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +warning: irrefutable if-let pattern + --> $DIR/top-level-alternation.rs:5:20 + | +LL | if let 0..=2 | 1 = 0 {} + | ^ + | + = note: `#[warn(irrefutable_let_patterns)]` on by default + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:10:15 + | +LL | | 0 => {} + | ^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:15:15 + | +LL | | Some(0) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:20:9 + | +LL | (0, 0) => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:40:9 + | +LL | _ => {} + | ^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:44:9 + | +LL | Some(_) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:45:9 + | +LL | None => {} + | ^^^^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:50:9 + | +LL | None + | ^^^^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:51:15 + | +LL | | Some(_) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/top-level-alternation.rs:55:9 + | +LL | 1..=2 => {}, + | ^^^^^ + +error: aborting due to 10 previous errors + From 5c7bd52a7824fd1177e0b5c65ad063a23657d8b4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 28 Nov 2019 16:56:45 +0000 Subject: [PATCH 5/8] Lint for redundant branches in or-patterns --- src/librustc_mir/hair/pattern/_match.rs | 40 ++++++++++++++----- src/librustc_mir/hair/pattern/check_match.rs | 13 +++++- .../ui/or-patterns/exhaustiveness-pass.rs | 15 ++++--- .../ui/or-patterns/exhaustiveness-pass.stderr | 38 +++++++++++++++++- 4 files changed, 87 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index e2ed925047442..37a9381271a8c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -455,6 +455,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { } /// A 2D matrix. +#[derive(Clone)] pub struct Matrix<'p, 'tcx>(Vec>); impl<'p, 'tcx> Matrix<'p, 'tcx> { @@ -1025,17 +1026,19 @@ impl<'tcx> Constructor<'tcx> { } #[derive(Clone, Debug)] -pub enum Usefulness<'tcx> { - Useful, +pub enum Usefulness<'tcx, 'p> { + /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. + Useful(Vec<&'p Pat<'tcx>>), + /// Carries a list of witnesses of non-exhaustiveness. UsefulWithWitness(Vec>), NotUseful, } -impl<'tcx> Usefulness<'tcx> { +impl<'tcx, 'p> Usefulness<'tcx, 'p> { fn new_useful(preference: WitnessPreference) -> Self { match preference { ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), - LeaveOutWitness => Useful, + LeaveOutWitness => Useful(vec![]), } } @@ -1602,7 +1605,7 @@ pub fn is_useful<'p, 'tcx>( v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, hir_id: HirId, -) -> Usefulness<'tcx> { +) -> Usefulness<'tcx, 'p> { let &Matrix(ref rows) = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); @@ -1623,11 +1626,26 @@ pub fn is_useful<'p, 'tcx>( // If the first pattern is an or-pattern, expand it. if let Some(vs) = v.expand_or_pat() { - return vs - .into_iter() - .map(|v| is_useful(cx, matrix, &v, witness_preference, hir_id)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful); + // We need to push the already-seen patterns into the matrix in order to detect redundant + // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. + let mut matrix = matrix.clone(); + let mut unreachable_pats = Vec::new(); + let mut any_is_useful = false; + for v in vs { + let res = is_useful(cx, &matrix, &v, witness_preference, hir_id); + match res { + Useful(pats) => { + any_is_useful = true; + unreachable_pats.extend(pats); + } + NotUseful => unreachable_pats.push(v.head()), + UsefulWithWitness(_) => { + bug!("Encountered or-pat in `v` during exhaustiveness checking") + } + } + matrix.push(v); + } + return if any_is_useful { Useful(unreachable_pats) } else { NotUseful }; } let (ty, span) = matrix @@ -1768,7 +1786,7 @@ fn is_useful_specialized<'p, 'tcx>( lty: Ty<'tcx>, witness_preference: WitnessPreference, hir_id: HirId, -) -> Usefulness<'tcx> { +) -> Usefulness<'tcx, 'p> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let ctor_wild_subpatterns = diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 62bc04b65f34f..a6a043c23dd06 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -468,7 +468,16 @@ fn check_arms<'p, 'tcx>( hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } - Useful => (), + Useful(unreachable_subpatterns) => { + for pat in unreachable_subpatterns { + cx.tcx.lint_hir( + lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.hir_id, + pat.span, + "unreachable pattern", + ); + } + } UsefulWithWitness(_) => bug!(), } if guard.is_none() { @@ -496,7 +505,7 @@ fn check_not_useful<'p, 'tcx>( } else { pats.into_iter().map(|w| w.single_pattern()).collect() }), - Useful => bug!(), + Useful(_) => bug!(), } } diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index d49ae74db51b5..5c4e239b5e39f 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -43,13 +43,16 @@ fn main() { _ => {} } - // FIXME(or_patterns): Redundancies not detected for now. match (0,) { - (1 | 1,) => {} + (1 + | 1,) => {} //~ ERROR unreachable _ => {} } match [0; 2] { - [0 | 0, 0 | 0] => {} + [0 + | 0 //~ ERROR unreachable + , 0 + | 0] => {} //~ ERROR unreachable _ => {} } match &[][..] { @@ -57,12 +60,14 @@ fn main() { [0, _] => {} [0, _, _] => {} [1, ..] => {} - [1 | 2, ..] => {} + [1 //~ ERROR unreachable + | 2, ..] => {} _ => {} } match Some(0) { Some(0) => {} - Some(0 | 1) => {} + Some(0 //~ ERROR unreachable + | 1) => {} _ => {} } } diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.stderr b/src/test/ui/or-patterns/exhaustiveness-pass.stderr index 1f4278c4b8098..7ca02862b4567 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-pass.stderr @@ -1,8 +1,44 @@ +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:48:12 + | +LL | | 1,) => {} + | ^ + | +note: lint level defined here + --> $DIR/exhaustiveness-pass.rs:4:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:55:15 + | +LL | | 0] => {} + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:53:15 + | +LL | | 0 + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:63:10 + | +LL | [1 + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-pass.rs:69:14 + | +LL | Some(0 + | ^ + error: or-patterns are not fully implemented yet --> $DIR/exhaustiveness-pass.rs:10:10 | LL | (0 | _,) => {} | ^^^^^ -error: aborting due to previous error +error: aborting due to 6 previous errors From a476af22e8f4ec6a95561f0243b2ebd2936ee557 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Nov 2019 12:51:34 +0000 Subject: [PATCH 6/8] Correct error on partially unreachable or-pat in `if let` --- src/librustc_mir/hair/pattern/check_match.rs | 22 +++++++++-------- .../usefulness/top-level-alternation.rs | 3 +-- .../usefulness/top-level-alternation.stderr | 24 +++++++++---------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index a6a043c23dd06..c65df62c824b3 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -414,16 +414,9 @@ fn check_arms<'p, 'tcx>( hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => { bug!() } - hir::MatchSource::IfLetDesugar { .. } => { - cx.tcx.lint_hir( - lint::builtin::IRREFUTABLE_LET_PATTERNS, - hir_pat.hir_id, - pat.span, - "irrefutable if-let pattern", - ); - } - hir::MatchSource::WhileLetDesugar => { + hir::MatchSource::IfLetDesugar { .. } + | hir::MatchSource::WhileLetDesugar => { // check which arm we're on. match arm_index { // The arm with the user-specified pattern. @@ -437,11 +430,20 @@ fn check_arms<'p, 'tcx>( } // The arm with the wildcard pattern. 1 => { + let msg = match source { + hir::MatchSource::IfLetDesugar { .. } => { + "irrefutable if-let pattern" + } + hir::MatchSource::WhileLetDesugar => { + "irrefutable while-let pattern" + } + _ => bug!(), + }; cx.tcx.lint_hir( lint::builtin::IRREFUTABLE_LET_PATTERNS, hir_pat.hir_id, pat.span, - "irrefutable while-let pattern", + msg, ); } _ => bug!(), diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.rs b/src/test/ui/pattern/usefulness/top-level-alternation.rs index 6ba9b45847797..5a7f82063b8ab 100644 --- a/src/test/ui/pattern/usefulness/top-level-alternation.rs +++ b/src/test/ui/pattern/usefulness/top-level-alternation.rs @@ -2,8 +2,7 @@ fn main() { while let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern - if let 0..=2 | 1 = 0 {} //~ WARN irrefutable if-let pattern - // this one ^ is incorrect + if let 0..=2 | 1 = 0 {} //~ ERROR unreachable pattern match 0u8 { 0 diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.stderr b/src/test/ui/pattern/usefulness/top-level-alternation.stderr index ef7327471212a..772927f42f577 100644 --- a/src/test/ui/pattern/usefulness/top-level-alternation.stderr +++ b/src/test/ui/pattern/usefulness/top-level-alternation.stderr @@ -10,67 +10,65 @@ note: lint level defined here LL | #![deny(unreachable_patterns)] | ^^^^^^^^^^^^^^^^^^^^ -warning: irrefutable if-let pattern +error: unreachable pattern --> $DIR/top-level-alternation.rs:5:20 | LL | if let 0..=2 | 1 = 0 {} | ^ - | - = note: `#[warn(irrefutable_let_patterns)]` on by default error: unreachable pattern - --> $DIR/top-level-alternation.rs:10:15 + --> $DIR/top-level-alternation.rs:9:15 | LL | | 0 => {} | ^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:15:15 + --> $DIR/top-level-alternation.rs:14:15 | LL | | Some(0) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:20:9 + --> $DIR/top-level-alternation.rs:19:9 | LL | (0, 0) => {} | ^^^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:40:9 + --> $DIR/top-level-alternation.rs:39:9 | LL | _ => {} | ^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:44:9 + --> $DIR/top-level-alternation.rs:43:9 | LL | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:45:9 + --> $DIR/top-level-alternation.rs:44:9 | LL | None => {} | ^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:50:9 + --> $DIR/top-level-alternation.rs:49:9 | LL | None | ^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:51:15 + --> $DIR/top-level-alternation.rs:50:15 | LL | | Some(_) => {} | ^^^^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:55:9 + --> $DIR/top-level-alternation.rs:54:9 | LL | 1..=2 => {}, | ^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 11 previous errors From ef087d96f0fd44ce83ac8c44d11bbe3faa8e1c6a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Nov 2019 13:02:14 +0000 Subject: [PATCH 7/8] Move recently changed tests to the correct file --- .../ui/or-patterns/exhaustiveness-pass.rs | 28 -------------- .../ui/or-patterns/exhaustiveness-pass.stderr | 38 +------------------ .../exhaustiveness-unreachable-pattern.rs | 28 ++++++++++++++ .../exhaustiveness-unreachable-pattern.stderr | 32 +++++++++++++++- 4 files changed, 60 insertions(+), 66 deletions(-) diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.rs b/src/test/ui/or-patterns/exhaustiveness-pass.rs index 5c4e239b5e39f..ce0fe6fc2a375 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.rs +++ b/src/test/ui/or-patterns/exhaustiveness-pass.rs @@ -42,32 +42,4 @@ fn main() { ((0, 0) | (1, 0),) => {} _ => {} } - - match (0,) { - (1 - | 1,) => {} //~ ERROR unreachable - _ => {} - } - match [0; 2] { - [0 - | 0 //~ ERROR unreachable - , 0 - | 0] => {} //~ ERROR unreachable - _ => {} - } - match &[][..] { - [0] => {} - [0, _] => {} - [0, _, _] => {} - [1, ..] => {} - [1 //~ ERROR unreachable - | 2, ..] => {} - _ => {} - } - match Some(0) { - Some(0) => {} - Some(0 //~ ERROR unreachable - | 1) => {} - _ => {} - } } diff --git a/src/test/ui/or-patterns/exhaustiveness-pass.stderr b/src/test/ui/or-patterns/exhaustiveness-pass.stderr index 7ca02862b4567..1f4278c4b8098 100644 --- a/src/test/ui/or-patterns/exhaustiveness-pass.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-pass.stderr @@ -1,44 +1,8 @@ -error: unreachable pattern - --> $DIR/exhaustiveness-pass.rs:48:12 - | -LL | | 1,) => {} - | ^ - | -note: lint level defined here - --> $DIR/exhaustiveness-pass.rs:4:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/exhaustiveness-pass.rs:55:15 - | -LL | | 0] => {} - | ^ - -error: unreachable pattern - --> $DIR/exhaustiveness-pass.rs:53:15 - | -LL | | 0 - | ^ - -error: unreachable pattern - --> $DIR/exhaustiveness-pass.rs:63:10 - | -LL | [1 - | ^ - -error: unreachable pattern - --> $DIR/exhaustiveness-pass.rs:69:14 - | -LL | Some(0 - | ^ - error: or-patterns are not fully implemented yet --> $DIR/exhaustiveness-pass.rs:10:10 | LL | (0 | _,) => {} | ^^^^^ -error: aborting due to 6 previous errors +error: aborting due to previous error diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs index 2cd8ca2dbac62..860c7a1bde5fb 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.rs @@ -48,4 +48,32 @@ fn main() { ((1..=4,),) => {}, //~ ERROR unreachable pattern _ => {}, } + + match (0,) { + (1 + | 1,) => {} //~ ERROR unreachable + _ => {} + } + match [0; 2] { + [0 + | 0 //~ ERROR unreachable + , 0 + | 0] => {} //~ ERROR unreachable + _ => {} + } + match &[][..] { + [0] => {} + [0, _] => {} + [0, _, _] => {} + [1, ..] => {} + [1 //~ ERROR unreachable + | 2, ..] => {} + _ => {} + } + match Some(0) { + Some(0) => {} + Some(0 //~ ERROR unreachable + | 1) => {} + _ => {} + } } diff --git a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr index a4d55d805c3c6..87f69a484bbbc 100644 --- a/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr +++ b/src/test/ui/or-patterns/exhaustiveness-unreachable-pattern.stderr @@ -70,11 +70,41 @@ error: unreachable pattern LL | ((1..=4,),) => {}, | ^^^^^^^^^^^ +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:54:12 + | +LL | | 1,) => {} + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:61:15 + | +LL | | 0] => {} + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:59:15 + | +LL | | 0 + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:69:10 + | +LL | [1 + | ^ + +error: unreachable pattern + --> $DIR/exhaustiveness-unreachable-pattern.rs:75:14 + | +LL | Some(0 + | ^ + error: or-patterns are not fully implemented yet --> $DIR/exhaustiveness-unreachable-pattern.rs:10:10 | LL | (0 | _,) => {} | ^^^^^ -error: aborting due to 12 previous errors +error: aborting due to 17 previous errors From 1c1bec2f6dbed0910b2e0ca19cffb92d95be4ee5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 29 Nov 2019 15:53:54 +0000 Subject: [PATCH 8/8] Remove top-level or-pattern hack --- src/librustc_mir/hair/pattern/check_match.rs | 182 ++++++++---------- .../usefulness/top-level-alternation.rs | 3 +- .../usefulness/top-level-alternation.stderr | 14 +- 3 files changed, 85 insertions(+), 114 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index c65df62c824b3..737af3e1358f4 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -139,39 +139,22 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| { let mut have_errors = false; - let inlined_arms: Vec<(Vec<_>, _)> = arms + let inlined_arms: Vec<_> = arms .iter() .map(|arm| { - ( - // HACK(or_patterns; Centril | dlrobertson): Remove this and - // correctly handle exhaustiveness checking for nested or-patterns. - match &arm.pat.kind { - hir::PatKind::Or(pats) => pats, - _ => std::slice::from_ref(&arm.pat), - } - .iter() - .map(|pat| { - let mut patcx = PatCtxt::new( - self.tcx, - self.param_env.and(self.identity_substs), - self.tables, - ); - patcx.include_lint_checks(); - let pattern = cx - .pattern_arena - .alloc(expand_pattern(cx, patcx.lower_pattern(&pat))) - as &_; - if !patcx.errors.is_empty() { - patcx.report_inlining_errors(pat.span); - have_errors = true; - } - (pattern, &**pat) - }) - .collect(), - arm.guard.as_ref().map(|g| match g { - hir::Guard::If(ref e) => &**e, - }), - ) + let mut patcx = PatCtxt::new( + self.tcx, + self.param_env.and(self.identity_substs), + self.tables, + ); + patcx.include_lint_checks(); + let pattern: &_ = + cx.pattern_arena.alloc(expand_pattern(cx, patcx.lower_pattern(&arm.pat))); + if !patcx.errors.is_empty() { + patcx.report_inlining_errors(arm.pat.span); + have_errors = true; + } + (pattern, &*arm.pat, arm.guard.is_some()) }) .collect(); @@ -399,95 +382,90 @@ fn pat_is_catchall(pat: &Pat) -> bool { // Check for unreachable patterns fn check_arms<'p, 'tcx>( cx: &mut MatchCheckCtxt<'p, 'tcx>, - arms: &[(Vec<(&'p super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], + arms: &[(&'p super::Pat<'tcx>, &hir::Pat, bool)], source: hir::MatchSource, ) -> Matrix<'p, 'tcx> { let mut seen = Matrix::empty(); let mut catchall = None; - for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() { - for &(pat, hir_pat) in pats { - let v = PatStack::from_pattern(pat); - - match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { - NotUseful => { - match source { - hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => { - bug!() - } - - hir::MatchSource::IfLetDesugar { .. } - | hir::MatchSource::WhileLetDesugar => { - // check which arm we're on. - match arm_index { - // The arm with the user-specified pattern. - 0 => { - cx.tcx.lint_hir( - lint::builtin::UNREACHABLE_PATTERNS, - hir_pat.hir_id, - pat.span, - "unreachable pattern", - ); - } - // The arm with the wildcard pattern. - 1 => { - let msg = match source { - hir::MatchSource::IfLetDesugar { .. } => { - "irrefutable if-let pattern" - } - hir::MatchSource::WhileLetDesugar => { - "irrefutable while-let pattern" - } - _ => bug!(), - }; - cx.tcx.lint_hir( - lint::builtin::IRREFUTABLE_LET_PATTERNS, - hir_pat.hir_id, - pat.span, - msg, - ); - } - _ => bug!(), + for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() { + let v = PatStack::from_pattern(pat); + + match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { + NotUseful => { + match source { + hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), + + hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => { + // check which arm we're on. + match arm_index { + // The arm with the user-specified pattern. + 0 => { + cx.tcx.lint_hir( + lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.hir_id, + pat.span, + "unreachable pattern", + ); } - } - - hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { - let mut err = cx.tcx.struct_span_lint_hir( - lint::builtin::UNREACHABLE_PATTERNS, - hir_pat.hir_id, - pat.span, - "unreachable pattern", - ); - // if we had a catchall pattern, hint at that - if let Some(catchall) = catchall { - err.span_label(pat.span, "unreachable pattern"); - err.span_label(catchall, "matches any value"); + // The arm with the wildcard pattern. + 1 => { + let msg = match source { + hir::MatchSource::IfLetDesugar { .. } => { + "irrefutable if-let pattern" + } + hir::MatchSource::WhileLetDesugar => { + "irrefutable while-let pattern" + } + _ => bug!(), + }; + cx.tcx.lint_hir( + lint::builtin::IRREFUTABLE_LET_PATTERNS, + hir_pat.hir_id, + pat.span, + msg, + ); } - err.emit(); + _ => bug!(), } - - // Unreachable patterns in try and await expressions occur when one of - // the arms are an uninhabited type. Which is OK. - hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } - } - Useful(unreachable_subpatterns) => { - for pat in unreachable_subpatterns { - cx.tcx.lint_hir( + + hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { + let mut err = cx.tcx.struct_span_lint_hir( lint::builtin::UNREACHABLE_PATTERNS, hir_pat.hir_id, pat.span, "unreachable pattern", ); + // if we had a catchall pattern, hint at that + if let Some(catchall) = catchall { + err.span_label(pat.span, "unreachable pattern"); + err.span_label(catchall, "matches any value"); + } + err.emit(); } + + // Unreachable patterns in try and await expressions occur when one of + // the arms are an uninhabited type. Which is OK. + hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } - UsefulWithWitness(_) => bug!(), } - if guard.is_none() { - seen.push(v); - if catchall.is_none() && pat_is_catchall(hir_pat) { - catchall = Some(pat.span); + Useful(unreachable_subpatterns) => { + for pat in unreachable_subpatterns { + cx.tcx.lint_hir( + lint::builtin::UNREACHABLE_PATTERNS, + hir_pat.hir_id, + pat.span, + "unreachable pattern", + ); } } + UsefulWithWitness(_) => bug!(), + } + if !has_guard { + seen.push(v); + if catchall.is_none() && pat_is_catchall(hir_pat) { + catchall = Some(pat.span); + } } } seen diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.rs b/src/test/ui/pattern/usefulness/top-level-alternation.rs index 5a7f82063b8ab..4b47b978930f3 100644 --- a/src/test/ui/pattern/usefulness/top-level-alternation.rs +++ b/src/test/ui/pattern/usefulness/top-level-alternation.rs @@ -46,8 +46,7 @@ fn main() { match Some(0u8) { Some(_) => {} None => {} - None //~ ERROR unreachable pattern - | Some(_) => {} //~ ERROR unreachable pattern + None | Some(_) => {} //~ ERROR unreachable pattern } match 0u8 { 1 | 2 => {}, diff --git a/src/test/ui/pattern/usefulness/top-level-alternation.stderr b/src/test/ui/pattern/usefulness/top-level-alternation.stderr index 772927f42f577..7c7c4fc4eba28 100644 --- a/src/test/ui/pattern/usefulness/top-level-alternation.stderr +++ b/src/test/ui/pattern/usefulness/top-level-alternation.stderr @@ -55,20 +55,14 @@ LL | None => {} error: unreachable pattern --> $DIR/top-level-alternation.rs:49:9 | -LL | None - | ^^^^ - -error: unreachable pattern - --> $DIR/top-level-alternation.rs:50:15 - | -LL | | Some(_) => {} - | ^^^^^^^ +LL | None | Some(_) => {} + | ^^^^^^^^^^^^^^ error: unreachable pattern - --> $DIR/top-level-alternation.rs:54:9 + --> $DIR/top-level-alternation.rs:53:9 | LL | 1..=2 => {}, | ^^^^^ -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors