From a5de5a604ed2175f91077ef40ad62f5da65c01f2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Sep 2019 13:49:14 +0200 Subject: [PATCH 01/17] Run `rustfmt` --- src/librustc_mir/hair/pattern/_match.rs | 736 +++++++++---------- src/librustc_mir/hair/pattern/check_match.rs | 237 +++--- 2 files changed, 497 insertions(+), 476 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 907c84b6f8cf0..322ab115cda44 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -155,7 +155,6 @@ /// + If some constructors are missing from the matrix, it turns out we don't need to do /// anything special (because we know none of the integers are actually wildcards: i.e., we /// can't span wildcards using ranges). - use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; @@ -163,31 +162,31 @@ use self::WitnessPreference::*; use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::Idx; +use super::{compare_const_vals, PatternFoldable, PatternFolder}; use super::{FieldPat, Pat, PatKind, PatRange}; -use super::{PatternFoldable, PatternFolder, compare_const_vals}; use rustc::hir::def_id::DefId; -use rustc::hir::{RangeEnd, HirId}; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const}; -use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size}; +use rustc::hir::{HirId, RangeEnd}; +use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; +use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable}; +use rustc::lint; +use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; use rustc::mir::Field; -use rustc::mir::interpret::{ConstValue, Scalar, truncate, AllocId, Pointer}; use rustc::util::common::ErrorReported; -use rustc::lint; use syntax::attr::{SignedInt, UnsignedInt}; use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; -use smallvec::{SmallVec, smallvec}; -use std::cmp::{self, Ordering, min, max}; +use smallvec::{smallvec, SmallVec}; +use std::cmp::{self, max, min, Ordering}; +use std::convert::TryInto; use std::fmt; use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; use std::u128; -use std::convert::TryInto; pub fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> { LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat) @@ -216,11 +215,8 @@ impl LiteralExpander<'tcx> { // the easy case, deref a reference (ConstValue::Scalar(Scalar::Ptr(p)), x, y) if x == y => { let alloc = self.tcx.alloc_map.lock().unwrap_memory(p.alloc_id); - ConstValue::ByRef { - alloc, - offset: p.offset, - } - }, + ConstValue::ByRef { alloc, offset: p.offset } + } // unsize array to slice if pattern is array but match value or other patterns are slice (ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => { assert_eq!(t, u); @@ -229,12 +225,11 @@ impl LiteralExpander<'tcx> { start: p.offset.bytes().try_into().unwrap(), end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(), } - }, + } // fat pointers stay the same - | (ConstValue::Slice { .. }, _, _) + (ConstValue::Slice { .. }, _, _) | (_, ty::Slice(_), ty::Slice(_)) - | (_, ty::Str, ty::Str) - => val, + | (_, ty::Str, ty::Str) => val, // FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used _ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty), } @@ -247,30 +242,27 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { match (&pat.ty.kind, &*pat.kind) { ( &ty::Ref(_, rty, _), - &PatKind::Constant { value: Const { - val, - ty: ty::TyS { kind: ty::Ref(_, crty, _), .. }, - } }, - ) => { - Pat { - ty: pat.ty, - span: pat.span, - kind: box PatKind::Deref { - subpattern: Pat { - ty: rty, - span: pat.span, - kind: box PatKind::Constant { value: self.tcx.mk_const(Const { + &PatKind::Constant { + value: Const { val, ty: ty::TyS { kind: ty::Ref(_, crty, _), .. } }, + }, + ) => Pat { + ty: pat.ty, + span: pat.span, + kind: box PatKind::Deref { + subpattern: Pat { + ty: rty, + span: pat.span, + kind: box PatKind::Constant { + value: self.tcx.mk_const(Const { val: self.fold_const_value_deref(*val, rty, crty), ty: rty, - }) }, - } - } - } - } - (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => { - s.fold_with(self) - } - _ => pat.super_fold_with(self) + }), + }, + }, + }, + }, + (_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self), + _ => pat.super_fold_with(self), } } } @@ -278,9 +270,8 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { impl<'tcx> Pat<'tcx> { fn is_wildcard(&self) -> bool { match *self.kind { - PatKind::Binding { subpattern: None, .. } | PatKind::Wild => - true, - _ => false + PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true, + _ => false, } } } @@ -316,15 +307,14 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { write!(f, "\n")?; let &Matrix(ref m) = self; - let pretty_printed_matrix: Vec> = m.iter().map(|row| { - row.iter().map(|pat| format!("{:?}", pat)).collect() - }).collect(); + let pretty_printed_matrix: Vec> = + m.iter().map(|row| row.iter().map(|pat| format!("{:?}", pat)).collect()).collect(); let column_count = m.iter().map(|row| row.len()).max().unwrap_or(0); assert!(m.iter().all(|row| row.len() == column_count)); - let column_widths: Vec = (0..column_count).map(|col| { - pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0) - }).collect(); + let column_widths: Vec = (0..column_count) + .map(|col| pretty_printed_matrix.iter().map(|row| row[col].len()).max().unwrap_or(0)) + .collect(); let total_width = column_widths.iter().cloned().sum::() + column_count * 3 + 1; let br = "+".repeat(total_width); @@ -345,7 +335,8 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { impl<'p, 'tcx> FromIterator; 2]>> for Matrix<'p, 'tcx> { fn from_iter(iter: T) -> Self - where T: IntoIterator; 2]>> + where + T: IntoIterator; 2]>>, { Matrix(iter.into_iter().collect()) } @@ -461,7 +452,7 @@ impl<'tcx> Constructor<'tcx> { VariantIdx::new(0) } ConstantValue(c, _) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), - _ => bug!("bad constructor {:?} for adt {:?}", self, adt) + _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } @@ -488,14 +479,14 @@ impl<'tcx> Constructor<'tcx> { pub enum Usefulness<'tcx> { Useful, UsefulWithWitness(Vec>), - NotUseful + NotUseful, } impl<'tcx> Usefulness<'tcx> { fn is_useful(&self) -> bool { match *self { NotUseful => false, - _ => true + _ => true, } } } @@ -503,7 +494,7 @@ impl<'tcx> Usefulness<'tcx> { #[derive(Copy, Clone, Debug)] pub enum WitnessPreference { ConstructWitness, - LeaveOutWitness + LeaveOutWitness, } #[derive(Copy, Clone, Debug)] @@ -558,16 +549,13 @@ impl<'tcx> Witness<'tcx> { mut self, cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor<'tcx>, - ty: Ty<'tcx>) - -> Self - { + ty: Ty<'tcx>, + ) -> Self { let sub_pattern_tys = constructor_sub_pattern_tys(cx, ctor, ty); - self.0.extend(sub_pattern_tys.into_iter().map(|ty| { - Pat { - ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - } + self.0.extend(sub_pattern_tys.into_iter().map(|ty| Pat { + ty, + span: DUMMY_SP, + kind: box PatKind::Wild, })); self.apply_constructor(cx, ctor, ty) } @@ -587,25 +575,21 @@ impl<'tcx> Witness<'tcx> { /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } fn apply_constructor<'a>( mut self, - cx: &MatchCheckCtxt<'a,'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor<'tcx>, - ty: Ty<'tcx>) - -> Self - { + ty: Ty<'tcx>, + ) -> Self { let arity = constructor_arity(cx, ctor, ty); let pat = { let len = self.0.len() as u64; let mut pats = self.0.drain((len - arity) as usize..).rev(); match ty.kind { - ty::Adt(..) | - ty::Tuple(..) => { - let pats = pats.enumerate().map(|(i, p)| { - FieldPat { - field: Field::new(i), - pattern: p - } - }).collect(); + ty::Adt(..) | ty::Tuple(..) => { + let pats = pats + .enumerate() + .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .collect(); if let ty::Adt(adt, substs) = ty.kind { if adt.is_enum() { @@ -613,7 +597,7 @@ impl<'tcx> Witness<'tcx> { adt_def: adt, substs, variant_index: ctor.variant_index_for_adt(cx, adt), - subpatterns: pats + subpatterns: pats, } } else { PatKind::Leaf { subpatterns: pats } @@ -623,37 +607,25 @@ impl<'tcx> Witness<'tcx> { } } - ty::Ref(..) => { - PatKind::Deref { subpattern: pats.nth(0).unwrap() } - } + ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, ty::Slice(_) | ty::Array(..) => { - PatKind::Slice { - prefix: pats.collect(), - slice: None, - suffix: vec![] - } + PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } } - _ => { - match *ctor { - ConstantValue(value, _) => PatKind::Constant { value }, - ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange { - lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)), - hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), - end, - }), - _ => PatKind::Wild, - } - } + _ => match *ctor { + ConstantValue(value, _) => PatKind::Constant { value }, + ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange { + lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)), + hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), + end, + }), + _ => PatKind::Wild, + }, } }; - self.0.push(Pat { - ty, - span: DUMMY_SP, - kind: Box::new(pat), - }); + self.0.push(Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }); self } @@ -672,37 +644,33 @@ fn all_constructors<'a, 'tcx>( ) -> Vec> { debug!("all_constructors({:?})", pcx.ty); let ctors = match pcx.ty.kind { - ty::Bool => { - [true, false].iter().map(|&b| { - ConstantValue(ty::Const::from_bool(cx.tcx, b), pcx.span) - }).collect() - } + ty::Bool => [true, false] + .iter() + .map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b), pcx.span)) + .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![Slice(len)] } } // Treat arrays of a constant but unknown length like slices. - ty::Array(ref sub_ty, _) | - ty::Slice(ref sub_ty) => { + ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => { if cx.is_uninhabited(sub_ty) { vec![Slice(0)] } else { - (0..pcx.max_slice_length+1).map(|length| Slice(length)).collect() + (0..pcx.max_slice_length + 1).map(|length| Slice(length)).collect() } } - ty::Adt(def, substs) if def.is_enum() => { - def.variants.iter() - .filter(|v| { - !cx.tcx.features().exhaustive_patterns || - !v.uninhabited_from(cx.tcx, substs, def.adt_kind()).contains(cx.tcx, cx.module) - }) - .map(|v| Variant(v.def_id)) - .collect() - } + ty::Adt(def, substs) if def.is_enum() => def + .variants + .iter() + .filter(|v| { + !cx.tcx.features().exhaustive_patterns + || !v + .uninhabited_from(cx.tcx, substs, def.adt_kind()) + .contains(cx.tcx, cx.module) + }) + .map(|v| Variant(v.def_id)) + .collect(), ty::Char => { vec![ // The valid Unicode Scalar Value ranges. @@ -822,15 +790,13 @@ where 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, - ), - _ => {}, + (_, 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 } => { @@ -907,7 +873,7 @@ impl<'tcx> IntRange<'tcx> { // This is a more general form of the previous branch. val } else { - return None + return None; }; let val = val ^ bias; Some(IntRange { range: val..=val, ty, span }) @@ -978,7 +944,7 @@ impl<'tcx> IntRange<'tcx> { } box PatKind::AscribeUserType { ref subpattern, .. } => { pat = subpattern; - }, + } _ => return None, } } @@ -991,7 +957,7 @@ impl<'tcx> IntRange<'tcx> { let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; 1u128 << (bits - 1) } - _ => 0 + _ => 0, } } @@ -1020,34 +986,43 @@ impl<'tcx> IntRange<'tcx> { param_env: ty::ParamEnv<'tcx>, ranges: Vec>, ) -> Vec> { - let ranges = ranges.into_iter().filter_map(|r| { - IntRange::from_ctor(tcx, param_env, &r).map(|i| i.range) - }); + let ranges = ranges + .into_iter() + .filter_map(|r| IntRange::from_ctor(tcx, param_env, &r).map(|i| i.range)); let mut remaining_ranges = vec![]; let ty = self.ty; let (lo, hi) = self.range.into_inner(); for subrange in ranges { let (subrange_lo, subrange_hi) = subrange.into_inner(); - if lo > subrange_hi || subrange_lo > hi { + if lo > subrange_hi || subrange_lo > hi { // The pattern doesn't intersect with the subrange at all, // so the subrange remains untouched. - remaining_ranges.push( - Self::range_to_ctor(tcx, ty, subrange_lo..=subrange_hi, self.span), - ); + remaining_ranges.push(Self::range_to_ctor( + tcx, + ty, + subrange_lo..=subrange_hi, + self.span, + )); } else { if lo > subrange_lo { // The pattern intersects an upper section of the // subrange, so a lower section will remain. - remaining_ranges.push( - Self::range_to_ctor(tcx, ty, subrange_lo..=(lo - 1), self.span), - ); + remaining_ranges.push(Self::range_to_ctor( + tcx, + ty, + subrange_lo..=(lo - 1), + self.span, + )); } if hi < subrange_hi { // The pattern intersects a lower section of the // subrange, so an upper section will remain. - remaining_ranges.push( - Self::range_to_ctor(tcx, ty, (hi + 1)..=subrange_hi, self.span), - ); + remaining_ranges.push(Self::range_to_ctor( + tcx, + ty, + (hi + 1)..=subrange_hi, + self.span, + )); } } } @@ -1205,12 +1180,13 @@ pub fn is_useful<'p, 'a, 'tcx>( } } else { NotUseful - } + }; }; assert!(rows.iter().all(|r| r.len() == v.len())); - let (ty, span) = rows.iter() + let (ty, span) = rows + .iter() .map(|r| (r[0].ty, r[0].span)) .find(|(ty, _)| !ty.references_error()) .unwrap_or((v[0].ty, v[0].span)); @@ -1244,16 +1220,25 @@ pub fn is_useful<'p, 'a, 'tcx>( if let Some(constructors) = pat_constructors(cx, v[0], pcx) { debug!("is_useful - expanding constructors: {:#?}", constructors); split_grouped_constructors( - cx.tcx, cx.param_env, constructors, matrix, pcx.ty, pcx.span, Some(hir_id), - ).into_iter().map(|c| - is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id) - ).find(|result| result.is_useful()).unwrap_or(NotUseful) + cx.tcx, + cx.param_env, + constructors, + matrix, + pcx.ty, + pcx.span, + Some(hir_id), + ) + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) } else { debug!("is_useful - expanding wildcard"); - let used_ctors: Vec> = rows.iter().flat_map(|row| { - pat_constructors(cx, row[0], pcx).unwrap_or(vec![]) - }).collect(); + let used_ctors: Vec> = rows + .iter() + .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) + .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 `_`). @@ -1284,35 +1269,47 @@ pub fn is_useful<'p, 'a, 'tcx>( // the set is empty, but we only fully construct them on-demand, // because they're rarely used and can be big. let cheap_missing_ctors = compute_missing_ctors( - MissingCtorsInfo::Emptiness, cx.tcx, cx.param_env, &all_ctors, &used_ctors, + MissingCtorsInfo::Emptiness, + cx.tcx, + cx.param_env, + &all_ctors, + &used_ctors, ); let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); - debug!("cheap_missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", - cheap_missing_ctors, is_privately_empty, is_declared_nonexhaustive); + debug!( + "cheap_missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", + cheap_missing_ctors, is_privately_empty, is_declared_nonexhaustive + ); // For privately empty and non-exhaustive enums, we work as if there were an "extra" // `_` constructor for the type, so we can never match over all constructors. - let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive || - (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); + let is_non_exhaustive = is_privately_empty + || is_declared_nonexhaustive + || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); if cheap_missing_ctors == MissingCtors::Empty && !is_non_exhaustive { split_grouped_constructors( - cx.tcx, cx.param_env, all_ctors, matrix, pcx.ty, DUMMY_SP, None, + 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, hir_id)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) } else { - let matrix = rows.iter().filter_map(|r| { - if r[0].is_wildcard() { - Some(SmallVec::from_slice(&r[1..])) - } else { - None - } - }).collect(); + let matrix = rows + .iter() + .filter_map(|r| { + if r[0].is_wildcard() { Some(SmallVec::from_slice(&r[1..])) } else { None } + }) + .collect(); match is_useful(cx, &matrix, &v[1..], witness, hir_id) { UsefulWithWitness(pats) => { let cx = &*cx; @@ -1363,35 +1360,43 @@ pub fn is_useful<'p, 'a, 'tcx>( let new_witnesses = if is_non_exhaustive || used_ctors.is_empty() { // All constructors are unused. Add wild patterns // rather than each individual constructor. - pats.into_iter().map(|mut witness| { - witness.0.push(Pat { - ty: pcx.ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - }); - witness - }).collect() + pats.into_iter() + .map(|mut witness| { + witness.0.push(Pat { + ty: pcx.ty, + span: DUMMY_SP, + kind: box PatKind::Wild, + }); + witness + }) + .collect() } else { let expensive_missing_ctors = compute_missing_ctors( - MissingCtorsInfo::Ctors, cx.tcx, cx.param_env, &all_ctors, &used_ctors, + MissingCtorsInfo::Ctors, + cx.tcx, + cx.param_env, + &all_ctors, + &used_ctors, ); if let MissingCtors::Ctors(missing_ctors) = expensive_missing_ctors { - pats.into_iter().flat_map(|witness| { - missing_ctors.iter().map(move |ctor| { - // Extends the witness with a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, this pushes the witness for `Some(_)`. - witness.clone().push_wild_constructor(cx, ctor, pcx.ty) + pats.into_iter() + .flat_map(|witness| { + missing_ctors.iter().map(move |ctor| { + // Extends the witness with a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, this pushes the witness for `Some(_)`. + witness.clone().push_wild_constructor(cx, ctor, pcx.ty) + }) }) - }).collect() + .collect() } else { bug!("cheap missing ctors") } }; UsefulWithWitness(new_witnesses) } - result => result + result => result, } } } @@ -1410,29 +1415,22 @@ fn is_useful_specialized<'p, 'a, 'tcx>( ) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); - let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| { - Pat { - ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - } - }).collect(); + let wild_patterns_owned: Vec<_> = + sub_pat_tys.iter().map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }).collect(); let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); - let matrix = Matrix( - m.iter() - .filter_map(|r| specialize(cx, &r, &ctor, &wild_patterns)) - .collect() - ); + let matrix = + Matrix(m.iter().filter_map(|r| specialize(cx, &r, &ctor, &wild_patterns)).collect()); match specialize(cx, v, &ctor, &wild_patterns) { Some(v) => match is_useful(cx, &matrix, &v, witness, hir_id) { UsefulWithWitness(witnesses) => UsefulWithWitness( - witnesses.into_iter() + witnesses + .into_iter() .map(|witness| witness.apply_constructor(cx, &ctor, lty)) - .collect() + .collect(), ), - result => result - } - None => NotUseful + result => result, + }, + None => NotUseful, } } @@ -1450,32 +1448,28 @@ fn pat_constructors<'tcx>( pcx: PatCtxt<'tcx>, ) -> Option>> { match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => - pat_constructors(cx, subpattern, pcx), + PatKind::AscribeUserType { ref subpattern, .. } => pat_constructors(cx, subpattern, pcx), PatKind::Binding { .. } | PatKind::Wild => None, PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(vec![Single]), PatKind::Variant { adt_def, variant_index, .. } => { Some(vec![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( - 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::Range(PatRange { lo, hi, end }) => Some(vec![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![ - Slice(length.eval_usize(cx.tcx, cx.param_env)) - ]), - _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty) + ty::Array(_, length) => Some(vec![Slice(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(Slice).collect()) } else { Some(vec![Slice(pat_len)]) } @@ -1498,13 +1492,11 @@ fn constructor_arity(cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor<'tcx>, ty ty::Slice(..) | ty::Array(..) => match *ctor { Slice(length) => length, ConstantValue(..) => 0, - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) - } + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), + }, ty::Ref(..) => 1, - ty::Adt(adt, _) => { - adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.len() as u64 - } - _ => 0 + ty::Adt(adt, _) => adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.len() as u64, + _ => 0, } } @@ -1523,8 +1515,8 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( ty::Slice(ty) | ty::Array(ty, _) => match *ctor { Slice(length) => (0..length).map(|_| ty).collect(), ConstantValue(..) => vec![], - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty) - } + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), + }, ty::Ref(_, rty, _) => vec![rty], ty::Adt(adt, substs) => { if adt.is_box() { @@ -1533,30 +1525,36 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( } 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, + 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() + } + }) + .collect() } } _ => vec![], @@ -1581,17 +1579,20 @@ fn slice_pat_covered_by_const<'tcx>( let n = n.eval_usize(tcx, param_env); let ptr = Pointer::new(AllocId(0), offset); alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap() - }, + } (ConstValue::Slice { data, start, end }, ty::Slice(t)) => { assert_eq!(*t, tcx.types.u8); let ptr = Pointer::new(AllocId(0), Size::from_bytes(start as u64)); data.get_bytes(&tcx, ptr, Size::from_bytes((end - start) as u64)).unwrap() - }, + } // FIXME(oli-obk): create a way to extract fat pointers from ByRef (_, ty::Slice(_)) => return Ok(false), _ => bug!( "slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}", - const_val, prefix, slice, suffix, + const_val, + prefix, + slice, + suffix, ), }; @@ -1600,9 +1601,10 @@ fn slice_pat_covered_by_const<'tcx>( return Ok(false); } - for (ch, pat) in - data[..prefix.len()].iter().zip(prefix).chain( - data[data.len()-suffix.len()..].iter().zip(suffix)) + for (ch, pat) in data[..prefix.len()] + .iter() + .zip(prefix) + .chain(data[data.len() - suffix.len()..].iter().zip(suffix)) { match pat.kind { box PatKind::Constant { value } => { @@ -1715,7 +1717,8 @@ fn split_grouped_constructors<'p, 'tcx>( let mut overlaps = vec![]; // `borders` is the set of borders between equivalence classes: each equivalence // class lies between 2 borders. - let row_borders = m.iter() + let row_borders = m + .iter() .flat_map(|row| { IntRange::from_pat(tcx, param_env, row[0]).map(|r| (r, row.len())) }) @@ -1745,8 +1748,8 @@ fn split_grouped_constructors<'p, 'tcx>( // We're going to iterate through every pair of borders, making sure that each // represents an interval of nonnegative length, and convert each such interval // into a constructor. - for IntRange { range, .. } in borders.windows(2).filter_map(|window| { - match (window[0], window[1]) { + for IntRange { range, .. } in + borders.windows(2).filter_map(|window| match (window[0], window[1]) { (Border::JustBefore(n), Border::JustBefore(m)) => { if n < m { Some(IntRange { range: n..=(m - 1), ty, span }) @@ -1758,8 +1761,8 @@ fn split_grouped_constructors<'p, 'tcx>( Some(IntRange { range: n..=u128::MAX, ty, span }) } (Border::AfterMax, _) => None, - } - }) { + }) + { split_ctors.push(IntRange::range_to_ctor(tcx, ty, range, span)); } } @@ -1788,10 +1791,13 @@ fn lint_overlapping_patterns( err.span_label(ctor_range.span, "overlapping patterns"); for int_range in overlaps { // Use the real type for user display of the ranges: - err.span_label(int_range.span, &format!( - "this range overlaps on `{}`", - IntRange::range_to_ctor(tcx, ty, int_range.range, DUMMY_SP).display(tcx), - )); + err.span_label( + int_range.span, + &format!( + "this range overlaps on `{}`", + IntRange::range_to_ctor(tcx, ty, int_range.range, DUMMY_SP).display(tcx), + ), + ); } err.emit(); } @@ -1809,8 +1815,9 @@ fn constructor_covered_by_range<'tcx>( _ => bug!("`constructor_covered_by_range` called with {:?}", pat), }; trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); - let cmp_from = |c_from| compare_const_vals(tcx, c_from, from, param_env, ty) - .map(|res| res != Ordering::Less); + let cmp_from = |c_from| { + compare_const_vals(tcx, c_from, from, param_env, ty).map(|res| res != Ordering::Less) + }; let cmp_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); macro_rules! some_or_ok { ($e:expr) => { @@ -1823,37 +1830,31 @@ fn constructor_covered_by_range<'tcx>( match *ctor { ConstantValue(value, _) => { let to = some_or_ok!(cmp_to(value)); - let end = (to == Ordering::Less) || - (end == RangeEnd::Included && to == Ordering::Equal); + let end = + (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); Ok(some_or_ok!(cmp_from(value)) && end) - }, + } ConstantRange(from, to, ty, RangeEnd::Included, _) => { - let to = some_or_ok!(cmp_to(ty::Const::from_bits( - tcx, - to, - ty::ParamEnv::empty().and(ty), - ))); - let end = (to == Ordering::Less) || - (end == RangeEnd::Included && to == Ordering::Equal); + let to = + some_or_ok!(cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty),))); + let end = + (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); Ok(some_or_ok!(cmp_from(ty::Const::from_bits( tcx, from, ty::ParamEnv::empty().and(ty), ))) && end) - }, + } ConstantRange(from, to, ty, RangeEnd::Excluded, _) => { - let to = some_or_ok!(cmp_to(ty::Const::from_bits( - tcx, - to, - ty::ParamEnv::empty().and(ty) - ))); - let end = (to == Ordering::Less) || - (end == RangeEnd::Excluded && to == Ordering::Equal); + let to = + some_or_ok!(cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty)))); + let end = + (to == Ordering::Less) || (end == RangeEnd::Excluded && to == Ordering::Equal); Ok(some_or_ok!(cmp_from(ty::Const::from_bits( tcx, from, - ty::ParamEnv::empty().and(ty))) - ) && end) + ty::ParamEnv::empty().and(ty) + ))) && end) } Single => Ok(true), _ => bug!(), @@ -1899,9 +1900,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( specialize(cx, ::std::slice::from_ref(&subpattern), constructor, wild_patterns) } - PatKind::Binding { .. } | PatKind::Wild => { - Some(SmallVec::from_slice(wild_patterns)) - } + PatKind::Binding { .. } | PatKind::Wild => Some(SmallVec::from_slice(wild_patterns)), PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; @@ -1915,9 +1914,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( Some(patterns_for_variant(cx, subpatterns, wild_patterns, false)) } - PatKind::Deref { ref subpattern } => { - Some(smallvec![subpattern]) - } + PatKind::Deref { ref subpattern } => Some(smallvec![subpattern]), PatKind::Constant { value } if constructor.is_slice() => { // We extract an `Option` for the pointer because slices of zero @@ -1925,39 +1922,28 @@ fn specialize<'p, 'a: 'p, 'tcx>( // just integers. The only time they should be pointing to memory // is when they are subslices of nonzero slices. let (alloc, offset, n, ty) = match value.ty.kind { - ty::Array(t, n) => { - match value.val { - ConstValue::ByRef { offset, alloc, .. } => ( - alloc, - offset, - n.eval_usize(cx.tcx, cx.param_env), - t, - ), - _ => span_bug!( - pat.span, - "array pattern is {:?}", value, - ), + ty::Array(t, n) => match value.val { + ConstValue::ByRef { offset, alloc, .. } => { + (alloc, offset, n.eval_usize(cx.tcx, cx.param_env), t) } + _ => span_bug!(pat.span, "array pattern is {:?}", value,), }, ty::Slice(t) => { match value.val { - ConstValue::Slice { data, start, end } => ( - data, - Size::from_bytes(start as u64), - (end - start) as u64, - t, - ), + ConstValue::Slice { data, start, end } => { + (data, Size::from_bytes(start as u64), (end - start) as u64, t) + } ConstValue::ByRef { .. } => { // FIXME(oli-obk): implement `deref` for `ConstValue` return None; - }, + } _ => span_bug!( pat.span, "slice pattern constant must be scalar pair but is {:?}", value, ), } - }, + } _ => span_bug!( pat.span, "unexpected const-val {:?} with ctor {:?}", @@ -1969,41 +1955,37 @@ fn specialize<'p, 'a: 'p, 'tcx>( // convert a constant slice/array pattern to a list of patterns. let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; let ptr = Pointer::new(AllocId(0), offset); - (0..n).map(|i| { - let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; - let scalar = alloc.read_scalar( - &cx.tcx, ptr, layout.size, - ).ok()?; - let scalar = scalar.not_undef().ok()?; - let value = ty::Const::from_scalar(cx.tcx, scalar, ty); - let pattern = Pat { - ty, - span: pat.span, - kind: box PatKind::Constant { value }, - }; - Some(&*cx.pattern_arena.alloc(pattern)) - }).collect() + (0..n) + .map(|i| { + let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?; + let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?; + let scalar = scalar.not_undef().ok()?; + let value = ty::Const::from_scalar(cx.tcx, scalar, ty); + let pattern = + Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; + Some(&*cx.pattern_arena.alloc(pattern)) + }) + .collect() } else { None } } - PatKind::Constant { .. } | - PatKind::Range { .. } => { + PatKind::Constant { .. } | PatKind::Range { .. } => { // If the constructor is a: // - Single value: add a row if the pattern contains the constructor. // - Range: add a row if the constructor intersects the pattern. if should_treat_range_exhaustively(cx.tcx, constructor) { - match (IntRange::from_ctor(cx.tcx, cx.param_env, constructor), - IntRange::from_pat(cx.tcx, cx.param_env, pat)) { - (Some(ctor), Some(pat)) => { - ctor.intersection(&pat).map(|_| { - let (pat_lo, pat_hi) = pat.range.into_inner(); - let (ctor_lo, ctor_hi) = ctor.range.into_inner(); - assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); - smallvec![] - }) - } + match ( + IntRange::from_ctor(cx.tcx, cx.param_env, constructor), + IntRange::from_pat(cx.tcx, cx.param_env, pat), + ) { + (Some(ctor), Some(pat)) => ctor.intersection(&pat).map(|_| { + let (pat_lo, pat_hi) = pat.range.into_inner(); + let (ctor_lo, ctor_hi) = ctor.range.into_inner(); + assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); + smallvec![] + }), _ => None, } } else { @@ -2019,39 +2001,49 @@ fn specialize<'p, 'a: 'p, 'tcx>( } } - PatKind::Array { ref prefix, ref slice, ref suffix } | - PatKind::Slice { ref prefix, ref slice, ref suffix } => { - match *constructor { - Slice(..) => { - let pat_len = prefix.len() + suffix.len(); - if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) { - if slice_count == 0 || slice.is_some() { - Some(prefix.iter().chain( - wild_patterns.iter().map(|p| *p) - .skip(prefix.len()) - .take(slice_count) - .chain(suffix.iter()) - ).collect()) - } else { - None - } + PatKind::Array { ref prefix, ref slice, ref suffix } + | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { + Slice(..) => { + let pat_len = prefix.len() + suffix.len(); + if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) { + if slice_count == 0 || slice.is_some() { + Some( + prefix + .iter() + .chain( + wild_patterns + .iter() + .map(|p| *p) + .skip(prefix.len()) + .take(slice_count) + .chain(suffix.iter()), + ) + .collect(), + ) } else { None } + } else { + None } - ConstantValue(cv, _) => { - match slice_pat_covered_by_const( - cx.tcx, pat.span, cv, prefix, slice, suffix, cx.param_env, - ) { - Ok(true) => Some(smallvec![]), - Ok(false) => None, - Err(ErrorReported) => None - } + } + ConstantValue(cv, _) => { + match slice_pat_covered_by_const( + cx.tcx, + pat.span, + cv, + prefix, + slice, + suffix, + cx.param_env, + ) { + Ok(true) => Some(smallvec![]), + Ok(false) => None, + Err(ErrorReported) => None, } - _ => span_bug!(pat.span, - "unexpected ctor {:?} for slice pat", constructor) } - } + _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), + }, PatKind::Or { .. } => { bug!("support for or-patterns has not been fully implemented yet."); @@ -2060,7 +2052,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head); head.map(|mut head| { - head.extend_from_slice(&r[1 ..]); + head.extend_from_slice(&r[1..]); head }) } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 77f3768172fb4..ecfe43c657475 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -1,25 +1,25 @@ -use super::_match::{MatchCheckCtxt, Matrix, expand_pattern, is_useful}; use super::_match::Usefulness::*; use super::_match::WitnessPreference::*; +use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix}; -use super::{PatCtxt, PatternError, PatKind}; +use super::{PatCtxt, PatKind, PatternError}; +use rustc::lint; use rustc::session::Session; -use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::subst::{InternalSubsts, SubstsRef}; -use rustc::lint; +use rustc::ty::{self, Ty, TyCtxt}; use rustc_errors::{Applicability, DiagnosticBuilder}; -use rustc::hir::HirId; use rustc::hir::def::*; use rustc::hir::def_id::DefId; -use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; +use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc::hir::HirId; use rustc::hir::{self, Pat}; use smallvec::smallvec; use std::slice; -use syntax_pos::{Span, DUMMY_SP, MultiSpan}; +use syntax_pos::{MultiSpan, Span, DUMMY_SP}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match tcx.hir().as_local_hir_id(def_id) { @@ -100,13 +100,15 @@ impl PatCtxt<'_, '_> { ::rustc::mir::interpret::struct_error( self.tcx.at(pat_span), "could not evaluate float literal (see issue #31407)", - ).emit(); + ) + .emit(); } PatternError::NonConstPath(span) => { ::rustc::mir::interpret::struct_error( self.tcx.at(span), "runtime values cannot be referenced in patterns", - ).emit(); + ) + .emit(); } } } @@ -123,12 +125,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { check_legality_of_bindings_in_at_patterns(self, pat); } - fn check_match( - &mut self, - scrut: &hir::Expr, - arms: &'tcx [hir::Arm], - source: hir::MatchSource - ) { + fn check_match(&mut self, scrut: &hir::Expr, arms: &'tcx [hir::Arm], source: hir::MatchSource) { for arm in arms { // First, check legality of move bindings. self.check_patterns(arm.guard.is_some(), &arm.pat); @@ -141,31 +138,41 @@ 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.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 inlined_arms: Vec<(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, + }), + ) }) - )).collect(); + .collect(); // Bail out early if inlining failed. if have_errors { @@ -191,17 +198,16 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { def_span = self.tcx.hir().span_if_local(def.did); if def.variants.len() < 4 && !def.variants.is_empty() { // keep around to point at the definition of non-covered variants - missing_variants = def.variants.iter() - .map(|variant| variant.ident) - .collect(); + missing_variants = + def.variants.iter().map(|variant| variant.ident).collect(); } let is_non_exhaustive_and_non_local = def.is_variant_list_non_exhaustive() && !def.did.is_local(); !(is_non_exhaustive_and_non_local) && def.variants.is_empty() - }, - _ => false + } + _ => false, } }; if !scrutinee_is_uninhabited { @@ -209,18 +215,25 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let mut err = create_e0004( self.tcx.sess, scrut.span, - format!("non-exhaustive patterns: {}", match missing_variants.len() { - 0 => format!("type `{}` is non-empty", pat_ty), - 1 => format!( - "pattern `{}` of type `{}` is not handled", - missing_variants[0].name, - pat_ty, - ), - _ => format!("multiple patterns of type `{}` are not handled", pat_ty), - }), + format!( + "non-exhaustive patterns: {}", + match missing_variants.len() { + 0 => format!("type `{}` is non-empty", pat_ty), + 1 => format!( + "pattern `{}` of type `{}` is not handled", + missing_variants[0].name, pat_ty, + ), + _ => format!( + "multiple patterns of type `{}` are not handled", + pat_ty + ), + } + ), + ); + err.help( + "ensure that all possible cases are being handled, \ + possibly by adding wildcards or more match arms", ); - err.help("ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms"); if let Some(sp) = def_span { err.span_label(sp, format!("`{}` defined here", pat_ty)); } @@ -248,16 +261,13 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { fn check_irrefutable(&self, pat: &'tcx Pat, origin: &str, sp: Option) { let module = self.tcx.hir().get_module_parent(pat.hir_id); MatchCheckCtxt::create_and_enter(self.tcx, self.param_env, module, |ref mut cx| { - let mut patcx = PatCtxt::new(self.tcx, - self.param_env.and(self.identity_substs), - self.tables); + let mut patcx = + PatCtxt::new(self.tcx, self.param_env.and(self.identity_substs), self.tables); 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![smallvec![ - &pattern - ]].into_iter().collect(); + let pats: Matrix<'_, '_> = vec![smallvec![&pattern]].into_iter().collect(); let witnesses = match check_not_useful(cx, pattern_ty, &pats, pat.hir_id) { Ok(_) => return, @@ -266,9 +276,12 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let joined_patterns = joined_uncovered_patterns(&witnesses); let mut err = struct_span_err!( - self.tcx.sess, pat.span, E0005, + self.tcx.sess, + pat.span, + E0005, "refutable pattern in {}: {} not covered", - origin, joined_patterns + origin, + joined_patterns ); let suggest_if_let = match &pat.kind { hir::PatKind::Path(hir::QPath::Resolved(None, path)) @@ -287,8 +300,10 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { }; if let (Some(span), true) = (sp, suggest_if_let) { - err.note("`let` bindings require an \"irrefutable pattern\", like a `struct` or \ - an `enum` with only one variant"); + err.note( + "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ + an `enum` with only one variant", + ); if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { err.span_suggestion( span, @@ -297,8 +312,10 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { Applicability::HasPlaceholders, ); } - err.note("for more information, visit \ - https://doc.rust-lang.org/book/ch18-02-refutability.html"); + err.note( + "for more information, visit \ + https://doc.rust-lang.org/book/ch18-02-refutability.html", + ); } adt_defined_here(cx, &mut err, pattern_ty, &witnesses); @@ -311,11 +328,10 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { /// This caused an irrefutable match failure in e.g. `let`. fn const_not_var(err: &mut DiagnosticBuilder<'_>, tcx: TyCtxt<'_>, pat: &Pat, path: &hir::Path) { let descr = path.res.descr(); - err.span_label(pat.span, format!( - "interpreted as {} {} pattern, not a new variable", - path.res.article(), - descr, - )); + err.span_label( + pat.span, + format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,), + ); err.span_suggestion( pat.span, @@ -342,19 +358,26 @@ fn check_for_bindings_named_same_as_variants(cx: &MatchVisitor<'_, '_>, pat: &Pa } let pat_ty = cx.tables.pat_ty(p); if let ty::Adt(edef, _) = pat_ty.kind { - if edef.is_enum() && edef.variants.iter().any(|variant| { - variant.ident == ident && variant.ctor_kind == CtorKind::Const - }) { + if edef.is_enum() + && edef.variants.iter().any(|variant| { + variant.ident == ident && variant.ctor_kind == CtorKind::Const + }) + { let ty_path = cx.tcx.def_path_str(edef.did); - let mut err = struct_span_warn!(cx.tcx.sess, p.span, E0170, + let mut err = struct_span_warn!( + cx.tcx.sess, + p.span, + E0170, "pattern binding `{}` is named the same as one \ - of the variants of the type `{}`", - ident, ty_path); + of the variants of the type `{}`", + ident, + ty_path + ); err.span_suggestion( p.span, "to match on the variant, qualify the path", format!("{}::{}", ty_path, ident), - Applicability::MachineApplicable + Applicability::MachineApplicable, ); err.emit(); } @@ -373,10 +396,8 @@ fn pat_is_catchall(pat: &Pat) -> bool { hir::PatKind::Binding(.., None) => true, hir::PatKind::Binding(.., Some(ref s)) => pat_is_catchall(s), hir::PatKind::Ref(ref s, _) => pat_is_catchall(s), - hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| { - pat_is_catchall(&p) - }), - _ => false + hir::PatKind::Tuple(ref v, _) => v.iter().all(|p| pat_is_catchall(&p)), + _ => false, } } @@ -395,8 +416,9 @@ fn check_arms<'tcx>( match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { NotUseful => { match source { - hir::MatchSource::IfDesugar { .. } | - hir::MatchSource::WhileDesugar => bug!(), + hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => { + bug!() + } hir::MatchSource::IfLetDesugar { .. } => { cx.tcx.lint_hir( lint::builtin::IRREFUTABLE_LET_PATTERNS, @@ -413,9 +435,11 @@ fn check_arms<'tcx>( 0 => { cx.tcx.lint_hir( lint::builtin::UNREACHABLE_PATTERNS, - hir_pat.hir_id, pat.span, - "unreachable pattern"); - }, + hir_pat.hir_id, + pat.span, + "unreachable pattern", + ); + } // The arm with the wildcard pattern. 1 => { cx.tcx.lint_hir( @@ -424,13 +448,12 @@ fn check_arms<'tcx>( pat.span, "irrefutable while-let pattern", ); - }, + } _ => bug!(), } } - hir::MatchSource::ForLoopDesugar | - hir::MatchSource::Normal => { + hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => { let mut err = cx.tcx.struct_span_lint_hir( lint::builtin::UNREACHABLE_PATTERNS, hir_pat.hir_id, @@ -447,12 +470,11 @@ fn check_arms<'tcx>( // 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 => {} + hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {} } } Useful => (), - UsefulWithWitness(_) => bug!() + UsefulWithWitness(_) => bug!(), } if guard.is_none() { seen.push(v); @@ -496,14 +518,15 @@ fn check_exhaustive<'tcx>( let joined_patterns = joined_uncovered_patterns(&witnesses); let mut err = create_e0004( - cx.tcx.sess, sp, + cx.tcx.sess, + sp, format!("non-exhaustive patterns: {} not covered", joined_patterns), ); err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); adt_defined_here(cx, &mut err, scrut_ty, &witnesses); err.help( "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms" + possibly by adding wildcards or more match arms", ) .emit(); } @@ -556,7 +579,7 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec // Don't point at variants that have already been covered due to other patterns to avoid // visual clutter. for pattern in patterns { - use PatKind::{AscribeUserType, Deref, Variant, Or, Leaf}; + use PatKind::{AscribeUserType, Deref, Leaf, Or, Variant}; match &*pattern.kind { AscribeUserType { subpattern, .. } | Deref { subpattern } => { covered.extend(maybe_point_at_variant(ty, slice::from_ref(&subpattern))); @@ -568,13 +591,15 @@ fn maybe_point_at_variant(ty: Ty<'_>, patterns: &[super::Pat<'_>]) -> Vec } covered.push(sp); - let pats = subpatterns.iter() + let pats = subpatterns + .iter() .map(|field_pattern| field_pattern.pattern.clone()) .collect::>(); covered.extend(maybe_point_at_variant(ty, &pats)); } Leaf { subpatterns } => { - let pats = subpatterns.iter() + let pats = subpatterns + .iter() .map(|field_pattern| field_pattern.pattern.clone()) .collect::>(); covered.extend(maybe_point_at_variant(ty, &pats)); @@ -659,7 +684,7 @@ fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pa struct AtBindingPatternVisitor<'a, 'b, 'tcx> { cx: &'a MatchVisitor<'b, 'tcx>, - bindings_allowed: bool + bindings_allowed: bool, } impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> { @@ -671,10 +696,14 @@ impl<'v> Visitor<'v> for AtBindingPatternVisitor<'_, '_, '_> { match pat.kind { hir::PatKind::Binding(.., ref subpat) => { if !self.bindings_allowed { - struct_span_err!(self.cx.tcx.sess, pat.span, E0303, - "pattern bindings are not allowed after an `@`") - .span_label(pat.span, "not allowed after `@`") - .emit(); + struct_span_err!( + self.cx.tcx.sess, + pat.span, + E0303, + "pattern bindings are not allowed after an `@`" + ) + .span_label(pat.span, "not allowed after `@`") + .emit(); } if subpat.is_some() { From af35934fa50f4d69fab46b98746dcbaa72aee19f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 27 Oct 2019 16:17:00 +0000 Subject: [PATCH 02/17] tidy --- src/librustc_mir/hair/pattern/_match.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 322ab115cda44..aeb581f0cba3d 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1537,13 +1537,14 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( (_, 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. + // 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).is_none() => { From 79865994170321d0237e13fbd57aced2090f16f5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 26 Sep 2019 20:47:05 +0200 Subject: [PATCH 03/17] Remove mention of old slice pattern syntax --- src/librustc_mir/hair/pattern/_match.rs | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index aeb581f0cba3d..d3e5f28f92fef 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -291,17 +291,17 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// Pretty-printer for matrices of patterns, example: -/// ++++++++++++++++++++++++++ -/// + _ + [] + -/// ++++++++++++++++++++++++++ -/// + true + [First] + -/// ++++++++++++++++++++++++++ -/// + true + [Second(true)] + -/// ++++++++++++++++++++++++++ -/// + false + [_] + -/// ++++++++++++++++++++++++++ -/// + _ + [_, _, ..tail] + -/// ++++++++++++++++++++++++++ +/// +++++++++++++++++++++++++++++ +/// + _ + [] + +/// +++++++++++++++++++++++++++++ +/// + true + [First] + +/// +++++++++++++++++++++++++++++ +/// + true + [Second(true)] + +/// +++++++++++++++++++++++++++++ +/// + false + [_] + +/// +++++++++++++++++++++++++++++ +/// + _ + [_, _, tail @ ..] + +/// +++++++++++++++++++++++++++++ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "\n")?; @@ -1439,7 +1439,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( /// 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. +/// `[a, b, tail @ ..]` can match a slice of length 2, 3, 4 and so on. /// /// Returns `None` in case of a catch-all, which can't be specialized. fn pat_constructors<'tcx>( @@ -1884,7 +1884,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( /// into `arity` patterns based on the constructor. For most patterns, the step is trivial, /// for instance tuple patterns are flattened and box patterns expand into their inner pattern. /// -/// OTOH, slice patterns with a subslice pattern (..tail) can be expanded into multiple +/// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple /// different patterns. /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. From 2665b6434e6c851f6bffecf83e35db22ee6bf6bd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 10:01:57 +0200 Subject: [PATCH 04/17] Clarify and fix the explanation of the algorithm There was a bit of confusion between individual patterns and lists of patterns, and index mismatches linked to that. This introduces a vocabulary of "pattern-stacks" to provide a clearer mental model of what is happening. This also adds examples. --- src/librustc_mir/hair/pattern/_match.rs | 198 ++++++++++++++++-------- 1 file changed, 132 insertions(+), 66 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index d3e5f28f92fef..1a619388f611c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -11,20 +11,24 @@ /// (without being so rigorous). /// /// The core of the algorithm revolves about a "usefulness" check. In particular, we -/// are trying to compute a predicate `U(P, p_{m + 1})` where `P` is a list of patterns -/// of length `m` for a compound (product) type with `n` components (we refer to this as -/// a matrix). `U(P, p_{m + 1})` represents whether, given an existing list of patterns -/// `p_1 ..= p_m`, adding a new pattern will be "useful" (that is, cover previously- +/// are trying to compute a predicate `U(P, p)` where `P` is a list of patterns (we refer to this as +/// a matrix). `U(P, p)` represents whether, given an existing list of patterns +/// `P_1 ..= P_m`, adding a new pattern `p` will be "useful" (that is, cover previously- /// uncovered values of the type). /// /// If we have this predicate, then we can easily compute both exhaustiveness of an /// entire set of patterns and the individual usefulness of each one. /// (a) the set of patterns is exhaustive iff `U(P, _)` is false (i.e., adding a wildcard /// match doesn't increase the number of values we're matching) -/// (b) a pattern `p_i` is not useful if `U(P[0..=(i-1), p_i)` is false (i.e., adding a +/// (b) a pattern `P_i` is not useful if `U(P[0..=(i-1), P_i)` is false (i.e., adding a /// pattern to those that have come before it doesn't increase the number of values /// we're matching). /// +/// During the course of the algorithm, the rows of the matrix won't just be individual patterns, +/// but rather partially-deconstructed patterns in the form of a list of patterns. The paper +/// calls those pattern-vectors, and we will call them pattern-stacks. The same holds for the +/// new pattern `p`. +/// /// For example, say we have the following: /// ``` /// // x: (Option, Result<()>) @@ -34,93 +38,155 @@ /// (None, Err(_)) => {} /// } /// ``` -/// Here, the matrix `P` is 3 x 2 (rows x columns). +/// Here, the matrix `P` starts as: /// [ -/// [Some(true), _], -/// [None, Err(())], -/// [None, Err(_)], +/// [(Some(true), _)], +/// [(None, Err(()))], +/// [(None, Err(_))], /// ] /// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering -/// `[Some(false), _]`, for instance). In addition, row 3 is not useful, because +/// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because /// all the values it covers are already covered by row 2. /// -/// To compute `U`, we must have two other concepts. -/// 1. `S(c, P)` is a "specialized matrix", where `c` is a constructor (like `Some` or -/// `None`). You can think of it as filtering `P` to just the rows whose *first* pattern -/// can cover `c` (and expanding OR-patterns into distinct patterns), and then expanding -/// the constructor into all of its components. -/// The specialization of a row vector is computed by `specialize`. +/// A list of patterns can be thought of as a stack, because we are mainly interested in the top of +/// the stack at any given point, and we can pop or apply constructors to get new pattern-stacks. +/// To match the paper, the top of the stack is at the beginning / on the left. +/// +/// There are two important operations on pattern-stacks necessary to understand the algorithm: +/// 1. We can pop a given constructor off the top of a stack. This operation is called +/// `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or +/// `None`) and `p` a pattern-stack. +/// If the pattern on top of the stack can cover `c`, this removes the constructor and +/// pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns. +/// Otherwise the pattern-stack is discarded. +/// This essentially filters those pattern-stacks whose top covers the constructor `c` and +/// discards the others. +/// +/// For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we +/// pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the +/// `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get +/// nothing back. /// -/// It is computed as follows. For each row `p_i` of P, we have four cases: -/// 1.1. `p_(i,1) = c(r_1, .., r_a)`. Then `S(c, P)` has a corresponding row: -/// r_1, .., r_a, p_(i,2), .., p_(i,n) -/// 1.2. `p_(i,1) = c'(r_1, .., r_a')` where `c ≠ c'`. Then `S(c, P)` has no -/// corresponding row. -/// 1.3. `p_(i,1) = _`. Then `S(c, P)` has a corresponding row: -/// _, .., _, p_(i,2), .., p_(i,n) -/// 1.4. `p_(i,1) = r_1 | r_2`. Then `S(c, P)` has corresponding rows inlined from: -/// S(c, (r_1, p_(i,2), .., p_(i,n))) -/// S(c, (r_2, p_(i,2), .., p_(i,n))) +/// This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` +/// on top of the stack, and we have four cases: +/// 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We +/// push onto the stack the arguments of this constructor, and return the result: +/// r_1, .., r_a, p_2, .., p_n +/// 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and +/// return nothing. +/// 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has +/// arguments (its arity), and return the resulting stack: +/// _, .., _, p_2, .., p_n +/// 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +/// stack: +/// S(c, (r_1, p_2, .., p_n)) +/// S(c, (r_2, p_2, .., p_n)) /// -/// 2. `D(P)` is a "default matrix". This is used when we know there are missing -/// constructor cases, but there might be existing wildcard patterns, so to check the -/// usefulness of the matrix, we have to check all its *other* components. -/// The default matrix is computed inline in `is_useful`. +/// 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is +/// a pattern-stack. +/// This is used when we know there are missing constructor cases, but there might be +/// existing wildcard patterns, so to check the usefulness of the matrix, we have to check +/// all its *other* components. /// -/// It is computed as follows. For each row `p_i` of P, we have three cases: -/// 1.1. `p_(i,1) = c(r_1, .., r_a)`. Then `D(P)` has no corresponding row. -/// 1.2. `p_(i,1) = _`. Then `D(P)` has a corresponding row: -/// p_(i,2), .., p_(i,n) -/// 1.3. `p_(i,1) = r_1 | r_2`. Then `D(P)` has corresponding rows inlined from: -/// D((r_1, p_(i,2), .., p_(i,n))) -/// D((r_2, p_(i,2), .., p_(i,n))) +/// It is computed as follows. We look at the pattern `p_1` on top of the stack, +/// and we have three cases: +/// 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing. +/// 1.2. `p_1 = _`. We return the rest of the stack: +/// p_2, .., p_n +/// 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting +/// stack. +/// D((r_1, p_2, .., p_n)) +/// D((r_2, p_2, .., p_n)) +/// +/// Note that the OR-patterns are not always used directly in Rust, but are used to derive the +/// exhaustive integer matching rules, so they're written here for posterity. +/// +/// Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by +/// working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with +/// the given constructor, and popping a wildcard keeps those rows that start with a wildcard. /// -/// Note that the OR-patterns are not always used directly in Rust, but are used to derive -/// the exhaustive integer matching rules, so they're written here for posterity. /// /// The algorithm for computing `U` /// ------------------------------- /// The algorithm is inductive (on the number of columns: i.e., components of tuple patterns). /// That means we're going to check the components from left-to-right, so the algorithm -/// operates principally on the first component of the matrix and new pattern `p_{m + 1}`. +/// operates principally on the first component of the matrix and new pattern-stack `p`. /// This algorithm is realised in the `is_useful` function. /// /// Base case. (`n = 0`, i.e., an empty tuple pattern) /// - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), -/// then `U(P, p_{m + 1})` is false. -/// - Otherwise, `P` must be empty, so `U(P, p_{m + 1})` is true. +/// then `U(P, p)` is false. +/// - Otherwise, `P` must be empty, so `U(P, p)` is true. /// /// Inductive step. (`n > 0`, i.e., whether there's at least one column /// [which may then be expanded into further columns later]) -/// We're going to match on the new pattern, `p_{m + 1}`. -/// - If `p_{m + 1} == c(r_1, .., r_a)`, then we have a constructor pattern. -/// Thus, the usefulness of `p_{m + 1}` can be reduced to whether it is useful when -/// we ignore all the patterns in `P` that involve other constructors. This is where -/// `S(c, P)` comes in: -/// `U(P, p_{m + 1}) := U(S(c, P), S(c, p_{m + 1}))` +/// We're going to match on the top of the new pattern-stack, `p_1`. +/// - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. +/// Then, the usefulness of `p_1` can be reduced to whether it is useful when +/// we ignore all the patterns in the first column of `P` that involve other constructors. +/// This is where `S(c, P)` comes in: +/// `U(P, p) := U(S(c, P), S(c, p))` /// This special case is handled in `is_useful_specialized`. -/// - If `p_{m + 1} == _`, then we have two more cases: -/// + All the constructors of the first component of the type exist within -/// all the rows (after having expanded OR-patterns). In this case: -/// `U(P, p_{m + 1}) := ∨(k ϵ constructors) U(S(k, P), S(k, p_{m + 1}))` -/// I.e., the pattern `p_{m + 1}` is only useful when all the constructors are -/// present *if* its later components are useful for the respective constructors -/// covered by `p_{m + 1}` (usually a single constructor, but all in the case of `_`). -/// + Some constructors are not present in the existing rows (after having expanded -/// OR-patterns). However, there might be wildcard patterns (`_`) present. Thus, we -/// are only really concerned with the other patterns leading with wildcards. This is -/// where `D` comes in: -/// `U(P, p_{m + 1}) := U(D(P), p_({m + 1},2), .., p_({m + 1},n))` -/// - If `p_{m + 1} == r_1 | r_2`, then the usefulness depends on each separately: -/// `U(P, p_{m + 1}) := U(P, (r_1, p_({m + 1},2), .., p_({m + 1},n))) -/// || U(P, (r_2, p_({m + 1},2), .., p_({m + 1},n)))` +/// +/// For example, if `P` is: +/// [ +/// [Some(true), _], +/// [None, 0], +/// ] +/// and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only +/// matches values that row 2 doesn't. For row 1 however, we need to dig into the +/// arguments of `Some` to know whether some new value is covered. So we compute +/// `U([[true, _]], [false, 0])`. +/// +/// - If `p_1 == _`, then we look at the list of constructors that appear in the first +/// component of the rows of `P`: +/// + If there are some constructors that aren't present, then we might think that the +/// wildcard `_` is useful, since it covers those constructors that weren't covered +/// before. +/// That's almost correct, but only works if there were no wildcards in those first +/// components. So we need to check that `p` is useful with respect to the rows that +/// start with a wildcard, if there are any. This is where `D` comes in: +/// `U(P, p) := U(D(P), D(p))` +/// +/// For example, if `P` is: +/// [ +/// [_, true, _], +/// [None, false, 1], +/// ] +/// and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we +/// only had row 2, we'd know that `p` is useful. However row 1 starts with a +/// wildcard, so we need to check whether `U([[true, _]], [false, 1])`. +/// +/// + Otherwise, all possible constructors (for the relevant type) are present. In this +/// case we must check whether the wildcard pattern covers any unmatched value. For +/// that, we can think of the `_` pattern as a big OR-pattern that covers all +/// possible constructors. For `Option`, that would mean `_ = None | Some(_)` for +/// example. The wildcard pattern is useful in this case if it is useful when +/// specialized to one of the possible constructors. So we compute: +/// `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` +/// +/// For example, if `P` is: +/// [ +/// [Some(true), _], +/// [None, false], +/// ] +/// and `p` is [_, false], both `None` and `Some` constructors appear in the first +/// components of `P`. We will therefore try popping both constructors in turn: we +/// compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]], +/// [false]) for the `None` constructor. The first case returns true, so we know that +/// `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched +/// before. +/// +/// - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: +/// `U(P, p) := U(P, (r_1, p_2, .., p_n)) +/// || U(P, (r_2, p_2, .., p_n))` /// /// Modifications to the algorithm /// ------------------------------ /// The algorithm in the paper doesn't cover some of the special cases that arise in Rust, for /// example uninhabited types and variable-length slice patterns. These are drawn attention to -/// throughout the code below. I'll make a quick note here about how exhaustive integer matching -/// is accounted for, though. +/// throughout the code below. I'll make a quick note here about how exhaustive integer matching is +/// accounted for, though. /// /// Exhaustive integer matching /// --------------------------- @@ -150,7 +216,7 @@ /// invalid, because we want a disjunction over every *integer* in each range, not just a /// disjunction over every range. This is a bit more tricky to deal with: essentially we need /// to form equivalence classes of subranges of the constructor range for which the behaviour -/// of the matrix `P` and new pattern `p_{m + 1}` are the same. This is described in more +/// of the matrix `P` and new pattern `p` are the same. This is described in more /// detail in `split_grouped_constructors`. /// + If some constructors are missing from the matrix, it turns out we don't need to do /// anything special (because we know none of the integers are actually wildcards: i.e., we From 403d6bd995dc6a5e708dfe28f36e900ff94723e1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 1 Nov 2019 15:44:58 +0000 Subject: [PATCH 05/17] Abstract out pattern stacks to make the code more legible --- src/librustc_mir/hair/pattern/_match.rs | 122 +++++++++++++------ src/librustc_mir/hair/pattern/check_match.rs | 11 +- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 1a619388f611c..8382ddeabc6fe 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -342,16 +342,69 @@ impl<'tcx> Pat<'tcx> { } } -/// A 2D matrix. Nx1 matrices are very common, which is why `SmallVec[_; 2]` -/// works well for each row. -pub struct Matrix<'p, 'tcx>(Vec; 2]>>); +/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` +/// works well. +#[derive(Debug, Clone)] +pub struct PatStack<'p, 'tcx>(SmallVec<[&'p Pat<'tcx>; 2]>); + +impl<'p, 'tcx> PatStack<'p, 'tcx> { + pub fn from_pattern(pat: &'p Pat<'tcx>) -> Self { + PatStack(smallvec![pat]) + } + + fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { + PatStack(vec) + } + + fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { + PatStack(SmallVec::from_slice(s)) + } + + fn is_empty(&self) -> bool { + self.0.is_empty() + } + + fn len(&self) -> usize { + self.0.len() + } + + fn head(&self) -> &'p Pat<'tcx> { + self.0[0] + } + + fn to_tail(&self) -> Self { + PatStack::from_slice(&self.0[1..]) + } + + fn iter(&self) -> impl Iterator> { + self.0.iter().map(|p| *p) + } +} + +impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { + fn default() -> Self { + PatStack(smallvec![]) + } +} + +impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { + fn from_iter(iter: T) -> Self + where + T: IntoIterator>, + { + PatStack(iter.into_iter().collect()) + } +} + +/// A 2D matrix. +pub struct Matrix<'p, 'tcx>(Vec>); impl<'p, 'tcx> Matrix<'p, 'tcx> { pub fn empty() -> Self { Matrix(vec![]) } - pub fn push(&mut self, row: SmallVec<[&'p Pat<'tcx>; 2]>) { + pub fn push(&mut self, row: PatStack<'p, 'tcx>) { self.0.push(row) } } @@ -399,10 +452,10 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> { } } -impl<'p, 'tcx> FromIterator; 2]>> for Matrix<'p, 'tcx> { +impl<'p, 'tcx> FromIterator> for Matrix<'p, 'tcx> { fn from_iter(iter: T) -> Self where - T: IntoIterator; 2]>>, + T: IntoIterator>, { Matrix(iter.into_iter().collect()) } @@ -1226,7 +1279,7 @@ fn compute_missing_ctors<'tcx>( pub fn is_useful<'p, 'a, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'p, 'tcx>, - v: &[&Pat<'tcx>], + v: &PatStack<'_, 'tcx>, witness: WitnessPreference, hir_id: HirId, ) -> Usefulness<'tcx> { @@ -1253,9 +1306,9 @@ pub fn is_useful<'p, 'a, 'tcx>( let (ty, span) = rows .iter() - .map(|r| (r[0].ty, r[0].span)) + .map(|r| (r.head().ty, r.head().span)) .find(|(ty, _)| !ty.references_error()) - .unwrap_or((v[0].ty, v[0].span)); + .unwrap_or((v.head().ty, v.head().span)); let pcx = PatCtxt { // TyErr is used to represent the type of wildcard patterns matching // against inaccessible (private) fields of structs, so that we won't @@ -1277,13 +1330,13 @@ 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, rows.iter().map(|r| r[0]).chain(Some(v[0]))), + max_slice_length: max_slice_length(cx, rows.iter().map(|r| r.head()).chain(Some(v.head()))), span, }; - debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]); + debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - if let Some(constructors) = pat_constructors(cx, v[0], pcx) { + if let Some(constructors) = pat_constructors(cx, v.head(), pcx) { debug!("is_useful - expanding constructors: {:#?}", constructors); split_grouped_constructors( cx.tcx, @@ -1303,7 +1356,7 @@ pub fn is_useful<'p, 'a, 'tcx>( let used_ctors: Vec> = rows .iter() - .flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![])) + .flat_map(|row| pat_constructors(cx, row.head(), pcx).unwrap_or(vec![])) .collect(); debug!("used_ctors = {:#?}", used_ctors); // `all_ctors` are all the constructors for the given type, which @@ -1372,11 +1425,9 @@ pub fn is_useful<'p, 'a, 'tcx>( } else { let matrix = rows .iter() - .filter_map(|r| { - if r[0].is_wildcard() { Some(SmallVec::from_slice(&r[1..])) } else { None } - }) + .filter_map(|r| if r.head().is_wildcard() { Some(r.to_tail()) } else { None }) .collect(); - match is_useful(cx, &matrix, &v[1..], witness, hir_id) { + match is_useful(cx, &matrix, &v.to_tail(), witness, hir_id) { UsefulWithWitness(pats) => { let cx = &*cx; // In this case, there's at least one "free" @@ -1473,7 +1524,7 @@ pub fn is_useful<'p, 'a, 'tcx>( fn is_useful_specialized<'p, 'a, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, &Matrix(ref m): &Matrix<'p, 'tcx>, - v: &[&Pat<'tcx>], + v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, lty: Ty<'tcx>, witness: WitnessPreference, @@ -1787,7 +1838,7 @@ fn split_grouped_constructors<'p, 'tcx>( let row_borders = m .iter() .flat_map(|row| { - IntRange::from_pat(tcx, param_env, row[0]).map(|r| (r, row.len())) + IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) }) .flat_map(|(range, row_len)| { let intersection = ctor_range.intersection(&range); @@ -1933,7 +1984,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( subpatterns: &'p [FieldPat<'tcx>], wild_patterns: &[&'p Pat<'tcx>], is_non_exhaustive: bool, -) -> SmallVec<[&'p Pat<'tcx>; 2]> { +) -> PatStack<'p, 'tcx> { let mut result = SmallVec::from_slice(wild_patterns); for subpat in subpatterns { @@ -1943,7 +1994,7 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( } debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result); - result + PatStack::from_vec(result) } /// This is the main specialization step. It expands the first pattern in the given row @@ -1954,20 +2005,20 @@ 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<'p, 'a: 'p, 'tcx>( +fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - r: &[&'p Pat<'tcx>], + r: &PatStack<'q, 'tcx>, constructor: &Constructor<'tcx>, wild_patterns: &[&'p Pat<'tcx>], -) -> Option; 2]>> { - let pat = &r[0]; +) -> Option> { + let pat = r.head(); let head = match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { - specialize(cx, ::std::slice::from_ref(&subpattern), constructor, wild_patterns) + specialize(cx, &PatStack::from_pattern(subpattern), constructor, wild_patterns) } - PatKind::Binding { .. } | PatKind::Wild => Some(SmallVec::from_slice(wild_patterns)), + PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::from_slice(wild_patterns)), PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; @@ -1981,7 +2032,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( Some(patterns_for_variant(cx, subpatterns, wild_patterns, false)) } - PatKind::Deref { ref subpattern } => Some(smallvec![subpattern]), + PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)), PatKind::Constant { value } if constructor.is_slice() => { // We extract an `Option` for the pointer because slices of zero @@ -2051,7 +2102,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( let (pat_lo, pat_hi) = pat.range.into_inner(); let (ctor_lo, ctor_hi) = ctor.range.into_inner(); assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); - smallvec![] + PatStack::default() }), _ => None, } @@ -2062,7 +2113,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( // range so intersection actually devolves into being covered // by the pattern. match constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat) { - Ok(true) => Some(smallvec![]), + Ok(true) => Some(PatStack::default()), Ok(false) | Err(ErrorReported) => None, } } @@ -2104,7 +2155,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( suffix, cx.param_env, ) { - Ok(true) => Some(smallvec![]), + Ok(true) => Some(PatStack::default()), Ok(false) => None, Err(ErrorReported) => None, } @@ -2116,10 +2167,11 @@ fn specialize<'p, 'a: 'p, 'tcx>( bug!("support for or-patterns has not been fully implemented yet."); } }; - debug!("specialize({:#?}, {:#?}) = {:#?}", r[0], wild_patterns, head); + debug!("specialize({:#?}, {:#?}) = {:#?}", r.head(), wild_patterns, head); - head.map(|mut head| { - head.extend_from_slice(&r[1..]); - head + head.map(|head| { + let mut head = head.0; + head.extend_from_slice(&r.0[1..]); + PatStack::from_vec(head) }) } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index ecfe43c657475..9d370554e86b4 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -1,6 +1,6 @@ use super::_match::Usefulness::*; use super::_match::WitnessPreference::*; -use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix}; +use super::_match::{expand_pattern, is_useful, MatchCheckCtxt, Matrix, PatStack}; use super::{PatCtxt, PatKind, PatternError}; @@ -16,7 +16,6 @@ use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc::hir::HirId; use rustc::hir::{self, Pat}; -use smallvec::smallvec; use std::slice; use syntax_pos::{MultiSpan, Span, DUMMY_SP}; @@ -251,7 +250,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { .iter() .filter(|&&(_, guard)| guard.is_none()) .flat_map(|arm| &arm.0) - .map(|pat| smallvec![pat.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); @@ -267,7 +266,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; let pattern = expand_pattern(cx, pattern); - let pats: Matrix<'_, '_> = vec![smallvec![&pattern]].into_iter().collect(); + 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, @@ -411,7 +410,7 @@ fn check_arms<'tcx>( let mut catchall = None; for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() { for &(pat, hir_pat) in pats { - let v = smallvec![pat]; + let v = PatStack::from_pattern(pat); match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { NotUseful => { @@ -493,7 +492,7 @@ fn check_not_useful( hir_id: HirId, ) -> Result<(), Vec>> { let wild_pattern = super::Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; - match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness, hir_id) { + 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] From 23ca38ed958d4ef3416a77a76f773897e433117b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 1 Nov 2019 16:33:34 +0000 Subject: [PATCH 06/17] Factor out some pattern-stack related functions --- src/librustc_mir/hair/pattern/_match.rs | 64 ++++++++++++++++++++----- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8382ddeabc6fe..2bbe097047cbd 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -379,6 +379,25 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { fn iter(&self) -> impl Iterator> { self.0.iter().map(|p| *p) } + + /// This computes `D(self)`. See top of the file for explanations. + fn specialize_wildcard(&self) -> Option { + if self.head().is_wildcard() { Some(self.to_tail()) } else { None } + } + + /// This computes `S(constructor, self)`. See top of the file for explanations. + fn specialize_constructor<'a, 'q>( + &self, + cx: &mut MatchCheckCtxt<'a, 'tcx>, + constructor: &Constructor<'tcx>, + wild_patterns: &[&'q Pat<'tcx>], + ) -> Option> + where + 'a: 'q, + 'p: 'q, + { + specialize(cx, self, constructor, wild_patterns) + } } impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { @@ -407,6 +426,30 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { pub fn push(&mut self, row: PatStack<'p, 'tcx>) { self.0.push(row) } + + /// This computes `D(self)`. See top of the file for explanations. + fn specialize_wildcard(&self) -> Self { + self.0.iter().filter_map(|r| r.specialize_wildcard()).collect() + } + + /// This computes `S(constructor, self)`. See top of the file for explanations. + fn specialize_constructor<'a, 'q>( + &self, + cx: &mut MatchCheckCtxt<'a, 'tcx>, + constructor: &Constructor<'tcx>, + wild_patterns: &[&'q Pat<'tcx>], + ) -> Matrix<'q, 'tcx> + where + 'a: 'q, + 'p: 'q, + { + Matrix( + self.0 + .iter() + .filter_map(|r| r.specialize_constructor(cx, constructor, wild_patterns)) + .collect(), + ) + } } /// Pretty-printer for matrices of patterns, example: @@ -1423,11 +1466,9 @@ pub fn is_useful<'p, 'a, 'tcx>( .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { - let matrix = rows - .iter() - .filter_map(|r| if r.head().is_wildcard() { Some(r.to_tail()) } else { None }) - .collect(); - match is_useful(cx, &matrix, &v.to_tail(), witness, hir_id) { + let matrix = matrix.specialize_wildcard(); + let v = v.to_tail(); + match is_useful(cx, &matrix, &v, witness, hir_id) { UsefulWithWitness(pats) => { let cx = &*cx; // In this case, there's at least one "free" @@ -1523,7 +1564,7 @@ pub fn is_useful<'p, 'a, 'tcx>( /// 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>, - &Matrix(ref m): &Matrix<'p, 'tcx>, + matrix: &Matrix<'p, 'tcx>, v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, lty: Ty<'tcx>, @@ -1535,9 +1576,8 @@ fn is_useful_specialized<'p, 'a, 'tcx>( let wild_patterns_owned: Vec<_> = sub_pat_tys.iter().map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }).collect(); let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); - let matrix = - Matrix(m.iter().filter_map(|r| specialize(cx, &r, &ctor, &wild_patterns)).collect()); - match specialize(cx, v, &ctor, &wild_patterns) { + let matrix = matrix.specialize_constructor(cx, &ctor, &wild_patterns); + match v.specialize_constructor(cx, &ctor, &wild_patterns) { Some(v) => match is_useful(cx, &matrix, &v, witness, hir_id) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses @@ -2013,7 +2053,7 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( ) -> Option> { let pat = r.head(); - let head = match *pat.kind { + let new_head = match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { specialize(cx, &PatStack::from_pattern(subpattern), constructor, wild_patterns) } @@ -2167,9 +2207,9 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( bug!("support for or-patterns has not been fully implemented yet."); } }; - debug!("specialize({:#?}, {:#?}) = {:#?}", r.head(), wild_patterns, head); + debug!("specialize({:#?}, {:#?}) = {:#?}", r.head(), wild_patterns, new_head); - head.map(|head| { + new_head.map(|head| { let mut head = head.0; head.extend_from_slice(&r.0[1..]); PatStack::from_vec(head) From 5e734bef414ec7d9482adababb5abe4d547ce867 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 16:07:23 +0200 Subject: [PATCH 07/17] Remove some redundancy --- src/librustc_mir/hair/pattern/_match.rs | 92 ++++++++++++++----------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 2bbe097047cbd..fb4dba2db5cf1 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -239,6 +239,7 @@ use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable}; use rustc::lint; use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; use rustc::mir::Field; +use rustc::util::captures::Captures; use rustc::util::common::ErrorReported; use syntax::attr::{SignedInt, UnsignedInt}; @@ -427,6 +428,11 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.0.push(row) } + /// Iterate over the first component of each row + fn heads<'a>(&'a self) -> impl Iterator> + Captures<'p> { + self.0.iter().map(|r| r.head()) + } + /// This computes `D(self)`. See top of the file for explanations. fn specialize_wildcard(&self) -> Self { self.0.iter().filter_map(|r| r.specialize_wildcard()).collect() @@ -635,6 +641,39 @@ impl<'tcx> Constructor<'tcx> { _ => bug!("bad constructor being displayed: `{:?}", self), } } + + fn wildcard_subpatterns<'a>( + &self, + cx: &MatchCheckCtxt<'a, 'tcx>, + ty: Ty<'tcx>, + ) -> Vec> { + constructor_sub_pattern_tys(cx, self, ty) + .into_iter() + .map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }) + .collect() + } + + /// This computes the arity of a constructor. The arity of a constructor + /// is how many subpattern patterns of that constructor should be expanded to. + /// + /// For instance, a tuple pattern `(_, 42, Some([]))` has the arity of 3. + /// A struct pattern's arity is the number of fields it contains, etc. + fn arity<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> u64 { + debug!("Constructor::arity({:#?}, {:?})", self, ty); + match ty.kind { + ty::Tuple(ref fs) => fs.len() as u64, + ty::Slice(..) | ty::Array(..) => match *self { + Slice(length) => length, + ConstantValue(..) => 0, + _ => bug!("bad slice pattern {:?} {:?}", self, ty), + }, + ty::Ref(..) => 1, + ty::Adt(adt, _) => { + adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64 + } + _ => 0, + } + } } #[derive(Clone, Debug)] @@ -713,12 +752,7 @@ impl<'tcx> Witness<'tcx> { ctor: &Constructor<'tcx>, ty: Ty<'tcx>, ) -> Self { - let sub_pattern_tys = constructor_sub_pattern_tys(cx, ctor, ty); - self.0.extend(sub_pattern_tys.into_iter().map(|ty| Pat { - ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - })); + self.0.extend(ctor.wildcard_subpatterns(cx, ty)); self.apply_constructor(cx, ctor, ty) } @@ -741,7 +775,7 @@ impl<'tcx> Witness<'tcx> { ctor: &Constructor<'tcx>, ty: Ty<'tcx>, ) -> Self { - let arity = constructor_arity(cx, ctor, ty); + let arity = ctor.arity(cx, ty); let pat = { let len = self.0.len() as u64; let mut pats = self.0.drain((len - arity) as usize..).rev(); @@ -1347,9 +1381,9 @@ pub fn is_useful<'p, 'a, 'tcx>( assert!(rows.iter().all(|r| r.len() == v.len())); - let (ty, span) = rows - .iter() - .map(|r| (r.head().ty, r.head().span)) + let (ty, span) = matrix + .heads() + .map(|r| (r.ty, r.span)) .find(|(ty, _)| !ty.references_error()) .unwrap_or((v.head().ty, v.head().span)); let pcx = PatCtxt { @@ -1373,7 +1407,7 @@ 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, rows.iter().map(|r| r.head()).chain(Some(v.head()))), + max_slice_length: max_slice_length(cx, matrix.heads().chain(Some(v.head()))), span, }; @@ -1397,10 +1431,8 @@ pub fn is_useful<'p, 'a, 'tcx>( } else { debug!("is_useful - expanding wildcard"); - let used_ctors: Vec> = rows - .iter() - .flat_map(|row| pat_constructors(cx, row.head(), pcx).unwrap_or(vec![])) - .collect(); + let used_ctors: Vec> = + matrix.heads().flat_map(|p| pat_constructors(cx, p, pcx).unwrap_or(vec![])).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 `_`). @@ -1572,9 +1604,8 @@ fn is_useful_specialized<'p, 'a, 'tcx>( hir_id: HirId, ) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); - let sub_pat_tys = constructor_sub_pattern_tys(cx, &ctor, lty); - let wild_patterns_owned: Vec<_> = - sub_pat_tys.iter().map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }).collect(); + + let wild_patterns_owned = ctor.wildcard_subpatterns(cx, lty); let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); let matrix = matrix.specialize_constructor(cx, &ctor, &wild_patterns); match v.specialize_constructor(cx, &ctor, &wild_patterns) { @@ -1637,26 +1668,6 @@ fn pat_constructors<'tcx>( } } -/// This computes the arity of a constructor. The arity of a constructor -/// is how many subpattern patterns of that constructor should be expanded to. -/// -/// For instance, a tuple pattern `(_, 42, Some([]))` has the arity of 3. -/// A struct pattern's arity is the number of fields it contains, etc. -fn constructor_arity(cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> u64 { - debug!("constructor_arity({:#?}, {:?})", ctor, ty); - match ty.kind { - ty::Tuple(ref fs) => fs.len() as u64, - ty::Slice(..) | ty::Array(..) => match *ctor { - Slice(length) => length, - ConstantValue(..) => 0, - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), - }, - ty::Ref(..) => 1, - ty::Adt(adt, _) => adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.len() as u64, - _ => 0, - } -} - /// This computes the types of the sub patterns that a constructor should be /// expanded to. /// @@ -1833,7 +1844,7 @@ fn split_grouped_constructors<'p, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, ctors: Vec>, - &Matrix(ref m): &Matrix<'p, 'tcx>, + matrix: &Matrix<'p, 'tcx>, ty: Ty<'tcx>, span: Span, hir_id: Option, @@ -1875,7 +1886,8 @@ fn split_grouped_constructors<'p, 'tcx>( let mut overlaps = vec![]; // `borders` is the set of borders between equivalence classes: each equivalence // class lies between 2 borders. - let row_borders = m + let row_borders = matrix + .0 .iter() .flat_map(|row| { IntRange::from_pat(tcx, param_env, row.head()).map(|r| (r, row.len())) From 0c02c19ac894669d5244dca69d7c177fe2a5752d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 16:37:01 +0200 Subject: [PATCH 08/17] Remove duplicate logic in compute_missing_constructors This is equivalent to the previous code in terms of performance. The expensive path is clearly identical. The fast path is also the same, because in both cases we loop until we get a non-empty `refined_ctors`, and then stop there. So the new code doesn't compute anything more than the previous did. --- src/librustc_mir/hair/pattern/_match.rs | 119 +++++++----------------- 1 file changed, 35 insertions(+), 84 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index fb4dba2db5cf1..a87965979e017 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1255,41 +1255,17 @@ impl<'tcx> IntRange<'tcx> { } } -// A request for missing constructor data in terms of either: -// - whether or not there any missing constructors; or -// - the actual set of missing constructors. -#[derive(PartialEq)] -enum MissingCtorsInfo { - Emptiness, - Ctors, -} - -// Used by `compute_missing_ctors`. -#[derive(Debug, PartialEq)] -enum MissingCtors<'tcx> { - Empty, - NonEmpty, - - // Note that the Vec can be empty. - Ctors(Vec>), -} - -// When `info` is `MissingCtorsInfo::Ctors`, compute a set of constructors -// equivalent to `all_ctors \ used_ctors`. When `info` is -// `MissingCtorsInfo::Emptiness`, just determines if that set is empty or not. -// (The split logic gives a performance win, because we always need to know if -// the set is empty, but we rarely need the full set, and it can be expensive -// to compute the full set.) -fn compute_missing_ctors<'tcx>( - info: MissingCtorsInfo, +type MissingConstructors<'a, 'tcx, F> = + std::iter::FlatMap>, Vec>, F>; +// Compute a set of constructors equivalent to `all_ctors \ used_ctors`. This +// returns an iterator, so that we only construct the whole set if needed. +fn compute_missing_ctors<'a, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - all_ctors: &Vec>, - used_ctors: &Vec>, -) -> MissingCtors<'tcx> { - let mut missing_ctors = vec![]; - - for req_ctor in all_ctors { + all_ctors: &'a Vec>, + used_ctors: &'a Vec>, +) -> MissingConstructors<'a, 'tcx, impl FnMut(&'a Constructor<'tcx>) -> Vec>> { + all_ctors.iter().flat_map(move |req_ctor| { let mut refined_ctors = vec![req_ctor.clone()]; for used_ctor in used_ctors { if used_ctor == req_ctor { @@ -1303,32 +1279,19 @@ fn compute_missing_ctors<'tcx>( } // If the constructor patterns that have been considered so far - // already cover the entire range of values, then we the + // 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 a constructor has not been matched, then it is missing. // We add `refined_ctors` instead of `req_ctor`, because then we can // provide more detailed error information about precisely which // ranges have been omitted. - if info == MissingCtorsInfo::Emptiness { - if !refined_ctors.is_empty() { - // The set is non-empty; return early. - return MissingCtors::NonEmpty; - } - } else { - missing_ctors.extend(refined_ctors); - } - } - - if info == MissingCtorsInfo::Emptiness { - // If we reached here, the set is empty. - MissingCtors::Empty - } else { - MissingCtors::Ctors(missing_ctors) - } + refined_ctors + }) } /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. @@ -1459,22 +1422,19 @@ pub fn is_useful<'p, 'a, 'tcx>( // needed for that case. // Missing constructors are those that are not matched by any - // non-wildcard patterns in the current column. We always determine if - // the set is empty, but we only fully construct them on-demand, - // because they're rarely used and can be big. - let cheap_missing_ctors = compute_missing_ctors( - MissingCtorsInfo::Emptiness, - cx.tcx, - cx.param_env, - &all_ctors, - &used_ctors, - ); + // 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 mut missing_ctors = + compute_missing_ctors(cx.tcx, cx.param_env, &all_ctors, &used_ctors).peekable(); let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); debug!( - "cheap_missing_ctors={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", - cheap_missing_ctors, is_privately_empty, is_declared_nonexhaustive + "missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", + missing_ctors.peek().is_none(), + is_privately_empty, + is_declared_nonexhaustive ); // For privately empty and non-exhaustive enums, we work as if there were an "extra" @@ -1483,7 +1443,8 @@ pub fn is_useful<'p, 'a, 'tcx>( || is_declared_nonexhaustive || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); - if cheap_missing_ctors == MissingCtors::Empty && !is_non_exhaustive { + if missing_ctors.peek().is_none() && !is_non_exhaustive { + drop(missing_ctors); // It was borrowing `all_ctors`, which we want to move. split_grouped_constructors( cx.tcx, cx.param_env, @@ -1561,28 +1522,18 @@ pub fn is_useful<'p, 'a, 'tcx>( }) .collect() } else { - let expensive_missing_ctors = compute_missing_ctors( - MissingCtorsInfo::Ctors, - cx.tcx, - cx.param_env, - &all_ctors, - &used_ctors, - ); - if let MissingCtors::Ctors(missing_ctors) = expensive_missing_ctors { - pats.into_iter() - .flat_map(|witness| { - missing_ctors.iter().map(move |ctor| { - // Extends the witness with a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, this pushes the witness for `Some(_)`. - witness.clone().push_wild_constructor(cx, ctor, pcx.ty) - }) + let missing_ctors: Vec<_> = missing_ctors.collect(); + pats.into_iter() + .flat_map(|witness| { + missing_ctors.iter().map(move |ctor| { + // Extends the witness with a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, this pushes the witness for `Some(_)`. + witness.clone().push_wild_constructor(cx, ctor, pcx.ty) }) - .collect() - } else { - bug!("cheap missing ctors") - } + }) + .collect() }; UsefulWithWitness(new_witnesses) } From b45458124ee662ac2746e091f28c3b70b95f7ef2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:36:42 +0200 Subject: [PATCH 09/17] Extract constructor application as a Constructor method --- src/librustc_mir/hair/pattern/_match.rs | 106 ++++++++++++++---------- 1 file changed, 64 insertions(+), 42 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index a87965979e017..093df57087cbe 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -674,6 +674,67 @@ impl<'tcx> Constructor<'tcx> { _ => 0, } } + + /// Apply a constructor to a list of patterns, yielding a new pattern. `pats` + /// must have as many elements as this constructor's arity. + /// + /// Examples: + /// self: Single + /// ty: tuple of 3 elements + /// pats: [10, 20, _] => (10, 20, _) + /// + /// self: Option::Some + /// ty: Option + /// pats: [false] => Some(false) + fn apply<'a>( + &self, + cx: &MatchCheckCtxt<'a, 'tcx>, + ty: Ty<'tcx>, + pats: impl IntoIterator>, + ) -> Pat<'tcx> { + let mut pats = pats.into_iter(); + let pat = match ty.kind { + ty::Adt(..) | ty::Tuple(..) => { + let pats = pats + .enumerate() + .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) + .collect(); + + if let ty::Adt(adt, substs) = ty.kind { + if adt.is_enum() { + PatKind::Variant { + adt_def: adt, + substs, + variant_index: self.variant_index_for_adt(cx, adt), + subpatterns: pats, + } + } else { + PatKind::Leaf { subpatterns: pats } + } + } else { + PatKind::Leaf { subpatterns: pats } + } + } + + ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, + + ty::Slice(_) | ty::Array(..) => { + PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } + } + + _ => match *self { + ConstantValue(value, _) => PatKind::Constant { value }, + ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange { + lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)), + hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), + end, + }), + _ => PatKind::Wild, + }, + }; + + Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } + } } #[derive(Clone, Debug)] @@ -778,50 +839,11 @@ impl<'tcx> Witness<'tcx> { let arity = ctor.arity(cx, ty); let pat = { let len = self.0.len() as u64; - let mut pats = self.0.drain((len - arity) as usize..).rev(); - - match ty.kind { - ty::Adt(..) | ty::Tuple(..) => { - let pats = pats - .enumerate() - .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) - .collect(); - - if let ty::Adt(adt, substs) = ty.kind { - if adt.is_enum() { - PatKind::Variant { - adt_def: adt, - substs, - variant_index: ctor.variant_index_for_adt(cx, adt), - subpatterns: pats, - } - } else { - PatKind::Leaf { subpatterns: pats } - } - } else { - PatKind::Leaf { subpatterns: pats } - } - } - - ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, - - ty::Slice(_) | ty::Array(..) => { - PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } - } - - _ => match *ctor { - ConstantValue(value, _) => PatKind::Constant { value }, - ConstantRange(lo, hi, ty, end, _) => PatKind::Range(PatRange { - lo: ty::Const::from_bits(cx.tcx, lo, ty::ParamEnv::empty().and(ty)), - hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), - end, - }), - _ => PatKind::Wild, - }, - } + let pats = self.0.drain((len - arity) as usize..).rev(); + ctor.apply(cx, ty, pats) }; - self.0.push(Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }); + self.0.push(pat); self } From 175976e2a2b03c3f347d4eff28661445c3c58372 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:37:42 +0200 Subject: [PATCH 10/17] Refactor "wild constructor" construction --- src/librustc_mir/hair/pattern/_match.rs | 73 +++++++++++-------------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 093df57087cbe..806575ba0be3c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -642,15 +642,17 @@ impl<'tcx> Constructor<'tcx> { } } + /// This returns one wildcard pattern for each argument to this constructor. fn wildcard_subpatterns<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>, - ) -> Vec> { - constructor_sub_pattern_tys(cx, self, ty) - .into_iter() - .map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }) - .collect() + ) -> impl Iterator> + DoubleEndedIterator { + constructor_sub_pattern_tys(cx, self, ty).into_iter().map(|ty| Pat { + ty, + span: DUMMY_SP, + kind: box PatKind::Wild, + }) } /// This computes the arity of a constructor. The arity of a constructor @@ -735,6 +737,12 @@ impl<'tcx> Constructor<'tcx> { Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } } + + /// Like `apply`, but where all the subpatterns are wildcards `_`. + fn apply_wildcards<'a>(&self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> { + let pats = self.wildcard_subpatterns(cx, ty).rev(); + self.apply(cx, ty, pats) + } } #[derive(Clone, Debug)] @@ -807,16 +815,6 @@ impl<'tcx> Witness<'tcx> { self.0.into_iter().next().unwrap() } - fn push_wild_constructor<'a>( - mut self, - cx: &MatchCheckCtxt<'a, 'tcx>, - ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, - ) -> Self { - self.0.extend(ctor.wildcard_subpatterns(cx, ty)); - self.apply_constructor(cx, ctor, ty) - } - /// Constructs a partial witness for a pattern given a list of /// patterns expanded by the specialization step. /// @@ -1530,33 +1528,28 @@ pub fn is_useful<'p, 'a, 'tcx>( // `(, , true)` - we are // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. - let new_witnesses = if is_non_exhaustive || used_ctors.is_empty() { - // All constructors are unused. Add wild patterns + let new_patterns = if is_non_exhaustive || used_ctors.is_empty() { + // All constructors are unused. Add a wild pattern // rather than each individual constructor. - pats.into_iter() - .map(|mut witness| { - witness.0.push(Pat { - ty: pcx.ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - }); - witness - }) - .collect() + vec![Pat { ty: pcx.ty, span: DUMMY_SP, kind: box PatKind::Wild }] } else { - let missing_ctors: Vec<_> = missing_ctors.collect(); - pats.into_iter() - .flat_map(|witness| { - missing_ctors.iter().map(move |ctor| { - // Extends the witness with a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, this pushes the witness for `Some(_)`. - witness.clone().push_wild_constructor(cx, ctor, pcx.ty) - }) - }) - .collect() + // Construct for each missing constructor a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, we get the pattern `Some(_)`. + missing_ctors.map(|ctor| ctor.apply_wildcards(cx, pcx.ty)).collect() }; + // Add the new patterns to each witness + let new_witnesses = pats + .into_iter() + .flat_map(|witness| { + new_patterns.iter().map(move |pat| { + let mut witness = witness.clone(); + witness.0.push(pat.clone()); + witness + }) + }) + .collect(); UsefulWithWitness(new_witnesses) } result => result, @@ -1578,7 +1571,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( ) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); - let wild_patterns_owned = ctor.wildcard_subpatterns(cx, lty); + let wild_patterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect(); let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); let matrix = matrix.specialize_constructor(cx, &ctor, &wild_patterns); match v.specialize_constructor(cx, &ctor, &wild_patterns) { From 57a656a46c279153a626dc5ee259c2c9c7d4af89 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:44:24 +0200 Subject: [PATCH 11/17] Clarify some variable names --- src/librustc_mir/hair/pattern/_match.rs | 65 ++++++++++++++----------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 806575ba0be3c..97fe8bc562752 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -391,13 +391,13 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { &self, cx: &mut MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, - wild_patterns: &[&'q Pat<'tcx>], + ctor_wild_subpatterns: &[&'q Pat<'tcx>], ) -> Option> where 'a: 'q, 'p: 'q, { - specialize(cx, self, constructor, wild_patterns) + specialize(cx, self, constructor, ctor_wild_subpatterns) } } @@ -443,7 +443,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { &self, cx: &mut MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, - wild_patterns: &[&'q Pat<'tcx>], + ctor_wild_subpatterns: &[&'q Pat<'tcx>], ) -> Matrix<'q, 'tcx> where 'a: 'q, @@ -452,7 +452,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { Matrix( self.0 .iter() - .filter_map(|r| r.specialize_constructor(cx, constructor, wild_patterns)) + .filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns)) .collect(), ) } @@ -1340,7 +1340,7 @@ pub fn is_useful<'p, 'a, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'_, 'tcx>, - witness: WitnessPreference, + witness_preference: WitnessPreference, hir_id: HirId, ) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; @@ -1353,7 +1353,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // the type of the tuple we're checking is inhabited or not. if v.is_empty() { return if rows.is_empty() { - match witness { + match witness_preference { ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), LeaveOutWitness => Useful, } @@ -1408,7 +1408,7 @@ pub fn is_useful<'p, 'a, 'tcx>( Some(hir_id), ) .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)) + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference, hir_id)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { @@ -1475,14 +1475,14 @@ pub fn is_useful<'p, 'a, 'tcx>( None, ) .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness, hir_id)) + .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(); - match is_useful(cx, &matrix, &v, witness, hir_id) { - UsefulWithWitness(pats) => { + match is_useful(cx, &matrix, &v, witness_preference, hir_id) { + UsefulWithWitness(witnesses) => { let cx = &*cx; // In this case, there's at least one "free" // constructor that is only matched against by @@ -1540,7 +1540,7 @@ pub fn is_useful<'p, 'a, 'tcx>( missing_ctors.map(|ctor| ctor.apply_wildcards(cx, pcx.ty)).collect() }; // Add the new patterns to each witness - let new_witnesses = pats + let new_witnesses = witnesses .into_iter() .flat_map(|witness| { new_patterns.iter().map(move |pat| { @@ -1566,16 +1566,16 @@ fn is_useful_specialized<'p, 'a, 'tcx>( v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, lty: Ty<'tcx>, - witness: WitnessPreference, + witness_preference: WitnessPreference, hir_id: HirId, ) -> Usefulness<'tcx> { debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); - let wild_patterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect(); - let wild_patterns: Vec<_> = wild_patterns_owned.iter().collect(); - let matrix = matrix.specialize_constructor(cx, &ctor, &wild_patterns); - match v.specialize_constructor(cx, &ctor, &wild_patterns) { - Some(v) => match is_useful(cx, &matrix, &v, witness, hir_id) { + let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect(); + let ctor_wild_subpatterns: Vec<_> = ctor_wild_subpatterns_owned.iter().collect(); + let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); + match v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) { + Some(v) => match is_useful(cx, &matrix, &v, witness_preference, hir_id) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses .into_iter() @@ -2000,10 +2000,10 @@ fn constructor_covered_by_range<'tcx>( fn patterns_for_variant<'p, 'a: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, subpatterns: &'p [FieldPat<'tcx>], - wild_patterns: &[&'p Pat<'tcx>], + ctor_wild_subpatterns: &[&'p Pat<'tcx>], is_non_exhaustive: bool, ) -> PatStack<'p, 'tcx> { - let mut result = SmallVec::from_slice(wild_patterns); + let mut result = SmallVec::from_slice(ctor_wild_subpatterns); for subpat in subpatterns { if !is_non_exhaustive || !cx.is_uninhabited(subpat.pattern.ty) { @@ -2011,7 +2011,10 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( } } - debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result); + debug!( + "patterns_for_variant({:#?}, {:#?}) = {:#?}", + subpatterns, ctor_wild_subpatterns, result + ); PatStack::from_vec(result) } @@ -2027,27 +2030,31 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, r: &PatStack<'q, 'tcx>, constructor: &Constructor<'tcx>, - wild_patterns: &[&'p Pat<'tcx>], + ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> Option> { let pat = r.head(); let new_head = match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { - specialize(cx, &PatStack::from_pattern(subpattern), constructor, wild_patterns) + specialize(cx, &PatStack::from_pattern(subpattern), constructor, ctor_wild_subpatterns) } - PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::from_slice(wild_patterns)), + PatKind::Binding { .. } | PatKind::Wild => { + Some(PatStack::from_slice(ctor_wild_subpatterns)) + } PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty); Some(Variant(variant.def_id)) .filter(|variant_constructor| variant_constructor == constructor) - .map(|_| patterns_for_variant(cx, subpatterns, wild_patterns, is_non_exhaustive)) + .map(|_| { + patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, is_non_exhaustive) + }) } PatKind::Leaf { ref subpatterns } => { - Some(patterns_for_variant(cx, subpatterns, wild_patterns, false)) + Some(patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns, false)) } PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)), @@ -2087,7 +2094,7 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( constructor, ), }; - if wild_patterns.len() as u64 == n { + if ctor_wild_subpatterns.len() as u64 == n { // convert a constant slice/array pattern to a list of patterns. let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?; let ptr = Pointer::new(AllocId(0), offset); @@ -2141,13 +2148,13 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { Slice(..) => { let pat_len = prefix.len() + suffix.len(); - if let Some(slice_count) = wild_patterns.len().checked_sub(pat_len) { + if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { Some( prefix .iter() .chain( - wild_patterns + ctor_wild_subpatterns .iter() .map(|p| *p) .skip(prefix.len()) @@ -2185,7 +2192,7 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( bug!("support for or-patterns has not been fully implemented yet."); } }; - debug!("specialize({:#?}, {:#?}) = {:#?}", r.head(), wild_patterns, new_head); + debug!("specialize({:#?}, {:#?}) = {:#?}", r.head(), ctor_wild_subpatterns, new_head); new_head.map(|head| { let mut head = head.0; From d663c276da262f4b3b7f67ac45257f0b451ab070 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:58:41 +0200 Subject: [PATCH 12/17] `specialize` conceptually operates on a single pattern --- src/librustc_mir/hair/pattern/_match.rs | 30 ++++++++++++------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 97fe8bc562752..8dace0bab7d63 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -397,7 +397,11 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { 'a: 'q, 'p: 'q, { - specialize(cx, self, constructor, ctor_wild_subpatterns) + 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..]); + new_head + }) } } @@ -2018,26 +2022,24 @@ fn patterns_for_variant<'p, 'a: 'p, 'tcx>( PatStack::from_vec(result) } -/// This is the main specialization step. It expands the first pattern in the given row +/// This is the main specialization step. It expands the pattern /// into `arity` patterns based on the constructor. For most patterns, the step is trivial, /// for instance tuple patterns are flattened and box patterns expand into their inner pattern. +/// Returns `None` if the pattern does not have the given constructor. /// /// OTOH, slice patterns with a subslice pattern (tail @ ..) can be expanded into multiple /// different patterns. /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. -fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( +fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - r: &PatStack<'q, 'tcx>, + pat: &'q Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> Option> { - let pat = r.head(); - - let new_head = match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => { - specialize(cx, &PatStack::from_pattern(subpattern), constructor, ctor_wild_subpatterns) - } + let result = match *pat.kind { + PatKind::AscribeUserType { ref subpattern, .. } => PatStack::from_pattern(subpattern) + .specialize_constructor(cx, constructor, ctor_wild_subpatterns), PatKind::Binding { .. } | PatKind::Wild => { Some(PatStack::from_slice(ctor_wild_subpatterns)) @@ -2192,11 +2194,7 @@ fn specialize<'p, 'a: 'p, 'q: 'p, 'tcx>( bug!("support for or-patterns has not been fully implemented yet."); } }; - debug!("specialize({:#?}, {:#?}) = {:#?}", r.head(), ctor_wild_subpatterns, new_head); + debug!("specialize({:#?}, {:#?}) = {:#?}", pat, ctor_wild_subpatterns, result); - new_head.map(|head| { - let mut head = head.0; - head.extend_from_slice(&r.0[1..]); - PatStack::from_vec(head) - }) + result } From cc4583dcd3c3206eda0a71a71730ff0c5501c64c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 19:09:30 +0200 Subject: [PATCH 13/17] Wording --- src/librustc_mir/hair/pattern/_match.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8dace0bab7d63..2e46cb9f8c915 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1509,7 +1509,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // 1) If the user is matching against a non-exhaustive // enum, there is no point in enumerating all possible // variants, because the user can't actually match - // against them himself, e.g., in an example like: + // against them themselves, e.g., in an example like: // ``` // let err: io::ErrorKind = ...; // match err { @@ -1885,9 +1885,9 @@ fn split_grouped_constructors<'p, 'tcx>( lint_overlapping_patterns(tcx, hir_id, ctor_range, ty, overlaps); - // We're going to iterate through every pair of borders, making sure that each - // represents an interval of nonnegative length, and convert each such interval - // into a constructor. + // We're going to iterate through every adjacent pair of borders, making sure that + // each represents an interval of nonnegative length, and convert each such + // interval into a constructor. for IntRange { range, .. } in borders.windows(2).filter_map(|window| match (window[0], window[1]) { (Border::JustBefore(n), Border::JustBefore(m)) => { From 92101b7655d915ae636efcc09d9df18783d8412e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 27 Oct 2019 16:36:50 +0000 Subject: [PATCH 14/17] Define a MissingConstructors struct for cleanliness --- src/librustc_mir/hair/pattern/_match.rs | 116 +++++++++++++++--------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 2e46cb9f8c915..986d2584e4e28 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1279,43 +1279,76 @@ impl<'tcx> IntRange<'tcx> { } } -type MissingConstructors<'a, 'tcx, F> = - std::iter::FlatMap>, Vec>, F>; -// Compute a set of constructors equivalent to `all_ctors \ used_ctors`. This -// returns an iterator, so that we only construct the whole set if needed. -fn compute_missing_ctors<'a, 'tcx>( +// A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. +struct MissingConstructors<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - all_ctors: &'a Vec>, - used_ctors: &'a Vec>, -) -> MissingConstructors<'a, 'tcx, impl FnMut(&'a Constructor<'tcx>) -> Vec>> { - all_ctors.iter().flat_map(move |req_ctor| { - let mut refined_ctors = vec![req_ctor.clone()]; - for used_ctor in used_ctors { - if used_ctor == req_ctor { - // 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, used_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); - } + all_ctors: Vec>, + used_ctors: Vec>, +} + +impl<'tcx> MissingConstructors<'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + all_ctors: Vec>, + used_ctors: Vec>, + ) -> Self { + MissingConstructors { tcx, param_env, all_ctors, used_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; + fn into_inner(self) -> (Vec>, Vec>) { + (self.all_ctors, self.used_ctors) + } + + fn is_empty(&self) -> bool { + self.iter().next().is_none() + } + /// Whether this contains all the constructors for the given type or only a + /// subset. + fn all_ctors_are_missing(&self) -> bool { + self.used_ctors.is_empty() + } + + /// Iterate over all_ctors \ used_ctors + fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { + self.all_ctors.iter().flat_map(move |req_ctor| { + let mut refined_ctors = vec![req_ctor.clone()]; + for used_ctor in &self.used_ctors { + if used_ctor == req_ctor { + // 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(self.tcx, self.param_env, used_ctor) + { + // Refine the required constructors for the type by subtracting + // the range defined by the current constructor pattern. + refined_ctors = interval.subtract_from(self.tcx, self.param_env, refined_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 a constructor has not been matched, then it is missing. - // We add `refined_ctors` instead of `req_ctor`, because then we can - // provide more detailed error information about precisely which - // ranges have been omitted. - refined_ctors - }) + // If a constructor has not been matched, then it is missing. + // We add `refined_ctors` instead of `req_ctor`, because then we can + // provide more detailed error information about precisely which + // ranges have been omitted. + refined_ctors + }) + } +} + +impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ctors: Vec<_> = self.iter().collect(); + write!(f, "{:?}", ctors) + } } /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. @@ -1426,6 +1459,9 @@ pub fn is_useful<'p, 'a, 'tcx>( let all_ctors = all_constructors(cx, pcx); debug!("all_ctors = {:#?}", all_ctors); + let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); + // `missing_ctors` is the set of constructors from the same type as the // first column of `matrix` that are matched only by wildcard patterns // from the first column. @@ -1449,14 +1485,11 @@ 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 mut missing_ctors = - compute_missing_ctors(cx.tcx, cx.param_env, &all_ctors, &used_ctors).peekable(); + let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); - let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); - let is_declared_nonexhaustive = cx.is_non_exhaustive_enum(pcx.ty) && !cx.is_local(pcx.ty); debug!( "missing_ctors.empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", - missing_ctors.peek().is_none(), + missing_ctors.is_empty(), is_privately_empty, is_declared_nonexhaustive ); @@ -1467,8 +1500,8 @@ pub fn is_useful<'p, 'a, 'tcx>( || is_declared_nonexhaustive || (pcx.ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); - if missing_ctors.peek().is_none() && !is_non_exhaustive { - drop(missing_ctors); // It was borrowing `all_ctors`, which we want to move. + if missing_ctors.is_empty() && !is_non_exhaustive { + let (all_ctors, _) = missing_ctors.into_inner(); split_grouped_constructors( cx.tcx, cx.param_env, @@ -1532,7 +1565,8 @@ pub fn is_useful<'p, 'a, 'tcx>( // `(, , true)` - we are // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. - let new_patterns = if is_non_exhaustive || used_ctors.is_empty() { + let new_patterns = if is_non_exhaustive || missing_ctors.all_ctors_are_missing() + { // All constructors are unused. Add a wild pattern // rather than each individual constructor. vec![Pat { ty: pcx.ty, span: DUMMY_SP, kind: box PatKind::Wild }] @@ -1541,7 +1575,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for // `Option::Some`, we get the pattern `Some(_)`. - missing_ctors.map(|ctor| ctor.apply_wildcards(cx, pcx.ty)).collect() + missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, pcx.ty)).collect() }; // Add the new patterns to each witness let new_witnesses = witnesses From b7fa9f12ca62fc4f95306fa0c233de948edf303c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 27 Oct 2019 16:58:04 +0000 Subject: [PATCH 15/17] Factor out constructor subtraction --- src/librustc_mir/hair/pattern/_match.rs | 63 ++++++++++++++----------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 986d2584e4e28..5d5cf9dfac7da 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -646,6 +646,41 @@ impl<'tcx> Constructor<'tcx> { } } + // Returns the set of constructors covered by `self` but not by + // anything in `other_ctors`. + fn subtract_ctors( + &self, + tcx: TyCtxt<'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); + } + + // 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 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. fn wildcard_subpatterns<'a>( &self, @@ -1313,33 +1348,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| { - let mut refined_ctors = vec![req_ctor.clone()]; - for used_ctor in &self.used_ctors { - if used_ctor == req_ctor { - // 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(self.tcx, self.param_env, used_ctor) - { - // Refine the required constructors for the type by subtracting - // the range defined by the current constructor pattern. - refined_ctors = interval.subtract_from(self.tcx, self.param_env, refined_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 a constructor has not been matched, then it is missing. - // We add `refined_ctors` instead of `req_ctor`, because then we can - // provide more detailed error information about precisely which - // ranges have been omitted. - refined_ctors + req_ctor.subtract_ctors(self.tcx, self.param_env, &self.used_ctors) }) } } From 8956c1ed1d95915ac17612cd93df38243b875790 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 27 Oct 2019 17:07:05 +0000 Subject: [PATCH 16/17] Factor out witness reconstruction --- src/librustc_mir/hair/pattern/_match.rs | 220 ++++++++++++++---------- 1 file changed, 131 insertions(+), 89 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 5d5cf9dfac7da..fbe265666d17c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -792,12 +792,82 @@ pub enum Usefulness<'tcx> { } impl<'tcx> Usefulness<'tcx> { + fn new_useful(preference: WitnessPreference) -> Self { + match preference { + ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), + LeaveOutWitness => Useful, + } + } + fn is_useful(&self) -> bool { match *self { NotUseful => false, _ => true, } } + + fn apply_constructor( + self, + cx: &MatchCheckCtxt<'_, 'tcx>, + ctor: &Constructor<'tcx>, + ty: Ty<'tcx>, + ) -> Self { + match self { + UsefulWithWitness(witnesses) => UsefulWithWitness( + witnesses + .into_iter() + .map(|witness| witness.apply_constructor(cx, &ctor, ty)) + .collect(), + ), + x => x, + } + } + + fn apply_wildcard(self, ty: Ty<'tcx>) -> Self { + match self { + UsefulWithWitness(witnesses) => { + let wild = Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; + UsefulWithWitness( + witnesses + .into_iter() + .map(|mut witness| { + witness.0.push(wild.clone()); + witness + }) + .collect(), + ) + } + x => x, + } + } + + fn apply_missing_ctors( + self, + cx: &MatchCheckCtxt<'_, 'tcx>, + ty: Ty<'tcx>, + missing_ctors: &MissingConstructors<'tcx>, + ) -> Self { + match self { + UsefulWithWitness(witnesses) => { + let new_patterns: Vec<_> = + missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, ty)).collect(); + // Add the new patterns to each witness + UsefulWithWitness( + witnesses + .into_iter() + .flat_map(|witness| { + new_patterns.iter().map(move |pat| { + let mut witness = witness.clone(); + witness.0.push(pat.clone()); + witness + }) + }) + .collect(), + ) + } + x => x, + } + } } #[derive(Copy, Clone, Debug)] @@ -1399,10 +1469,7 @@ pub fn is_useful<'p, 'a, 'tcx>( // the type of the tuple we're checking is inhabited or not. if v.is_empty() { return if rows.is_empty() { - match witness_preference { - ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]), - LeaveOutWitness => Useful, - } + Usefulness::new_useful(witness_preference) } else { NotUseful }; @@ -1527,79 +1594,62 @@ pub fn is_useful<'p, 'a, 'tcx>( } else { let matrix = matrix.specialize_wildcard(); let v = v.to_tail(); - match is_useful(cx, &matrix, &v, witness_preference, hir_id) { - UsefulWithWitness(witnesses) => { - let cx = &*cx; - // In this case, there's at least one "free" - // constructor that is only matched against by - // wildcard patterns. - // - // There are 2 ways we can report a witness here. - // Commonly, we can report all the "free" - // constructors as witnesses, e.g., if we have: - // - // ``` - // enum Direction { N, S, E, W } - // let Direction::N = ...; - // ``` - // - // we can report 3 witnesses: `S`, `E`, and `W`. - // - // However, there are 2 cases where we don't want - // to do this and instead report a single `_` witness: - // - // 1) If the user is matching against a non-exhaustive - // enum, there is no point in enumerating all possible - // variants, because the user can't actually match - // against them themselves, e.g., in an example like: - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // we don't want to show every possible IO error, - // but instead have `_` as the witness (this is - // actually *required* if the user specified *all* - // IO errors, but is probably what we want in every - // case). - // - // 2) If the user didn't actually specify a constructor - // in this arm, e.g., in - // ``` - // let x: (Direction, Direction, bool) = ...; - // let (_, _, false) = x; - // ``` - // we don't want to show all 16 possible witnesses - // `(, , true)` - we are - // satisfied with `(_, _, true)`. In this case, - // `used_ctors` is empty. - let new_patterns = if is_non_exhaustive || missing_ctors.all_ctors_are_missing() - { - // All constructors are unused. Add a wild pattern - // rather than each individual constructor. - vec![Pat { ty: pcx.ty, span: DUMMY_SP, kind: box PatKind::Wild }] - } else { - // Construct for each missing constructor a "wild" version of this - // constructor, that matches everything that can be built with - // it. For example, if `ctor` is a `Constructor::Variant` for - // `Option::Some`, we get the pattern `Some(_)`. - missing_ctors.iter().map(|ctor| ctor.apply_wildcards(cx, pcx.ty)).collect() - }; - // Add the new patterns to each witness - let new_witnesses = witnesses - .into_iter() - .flat_map(|witness| { - new_patterns.iter().map(move |pat| { - let mut witness = witness.clone(); - witness.0.push(pat.clone()); - witness - }) - }) - .collect(); - UsefulWithWitness(new_witnesses) - } - result => result, + let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id); + + // In this case, there's at least one "free" + // constructor that is only matched against by + // wildcard patterns. + // + // There are 2 ways we can report a witness here. + // Commonly, we can report all the "free" + // constructors as witnesses, e.g., if we have: + // + // ``` + // enum Direction { N, S, E, W } + // let Direction::N = ...; + // ``` + // + // we can report 3 witnesses: `S`, `E`, and `W`. + // + // However, there are 2 cases where we don't want + // to do this and instead report a single `_` witness: + // + // 1) If the user is matching against a non-exhaustive + // enum, there is no point in enumerating all possible + // variants, because the user can't actually match + // against them themselves, e.g., in an example like: + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // we don't want to show every possible IO error, + // but instead have `_` as the witness (this is + // actually *required* if the user specified *all* + // IO errors, but is probably what we want in every + // case). + // + // 2) If the user didn't actually specify a constructor + // in this arm, e.g., in + // ``` + // let x: (Direction, Direction, bool) = ...; + // let (_, _, false) = x; + // ``` + // we don't want to show all 16 possible witnesses + // `(, , true)` - we are + // satisfied with `(_, _, true)`. In this case, + // `used_ctors` is empty. + if is_non_exhaustive || missing_ctors.all_ctors_are_missing() { + // All constructors are unused. Add a wild pattern + // rather than each individual constructor. + usefulness.apply_wildcard(pcx.ty) + } else { + // Construct for each missing constructor a "wild" version of this + // constructor, that matches everything that can be built with + // it. For example, if `ctor` is a `Constructor::Variant` for + // `Option::Some`, we get the pattern `Some(_)`. + usefulness.apply_missing_ctors(cx, pcx.ty, &missing_ctors) } } } @@ -1621,18 +1671,10 @@ fn is_useful_specialized<'p, 'a, 'tcx>( let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect(); let ctor_wild_subpatterns: Vec<_> = ctor_wild_subpatterns_owned.iter().collect(); let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns); - match v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns) { - Some(v) => match is_useful(cx, &matrix, &v, witness_preference, hir_id) { - UsefulWithWitness(witnesses) => UsefulWithWitness( - witnesses - .into_iter() - .map(|witness| witness.apply_constructor(cx, &ctor, lty)) - .collect(), - ), - result => result, - }, - None => NotUseful, - } + 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) } /// Determines the constructors that the given pattern can be specialized to. From a96d7005c6d1e4b1977e8bf93e28a886ceb443eb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 27 Oct 2019 17:14:43 +0000 Subject: [PATCH 17/17] Clarifications suggested by varkor --- src/librustc_mir/hair/pattern/_match.rs | 32 +++++++++++++------------ 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index fbe265666d17c..8e06955c7cdc4 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -720,23 +720,25 @@ impl<'tcx> Constructor<'tcx> { /// must have as many elements as this constructor's arity. /// /// Examples: - /// self: Single - /// ty: tuple of 3 elements - /// pats: [10, 20, _] => (10, 20, _) + /// `self`: `Constructor::Single` + /// `ty`: `(u32, u32, u32)` + /// `pats`: `[10, 20, _]` + /// returns `(10, 20, _)` /// - /// self: Option::Some - /// ty: Option - /// pats: [false] => Some(false) + /// `self`: `Constructor::Variant(Option::Some)` + /// `ty`: `Option` + /// `pats`: `[false]` + /// returns `Some(false)` fn apply<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>, pats: impl IntoIterator>, ) -> Pat<'tcx> { - let mut pats = pats.into_iter(); + let mut subpatterns = pats.into_iter(); let pat = match ty.kind { ty::Adt(..) | ty::Tuple(..) => { - let pats = pats + let subpatterns = subpatterns .enumerate() .map(|(i, p)| FieldPat { field: Field::new(i), pattern: p }) .collect(); @@ -747,20 +749,20 @@ impl<'tcx> Constructor<'tcx> { adt_def: adt, substs, variant_index: self.variant_index_for_adt(cx, adt), - subpatterns: pats, + subpatterns, } } else { - PatKind::Leaf { subpatterns: pats } + PatKind::Leaf { subpatterns } } } else { - PatKind::Leaf { subpatterns: pats } + PatKind::Leaf { subpatterns } } } - ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, + ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.nth(0).unwrap() }, ty::Slice(_) | ty::Array(..) => { - PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } + PatKind::Slice { prefix: subpatterns.collect(), slice: None, suffix: vec![] } } _ => match *self { @@ -779,8 +781,8 @@ 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 pats = self.wildcard_subpatterns(cx, ty).rev(); - self.apply(cx, ty, pats) + let subpatterns = self.wildcard_subpatterns(cx, ty).rev(); + self.apply(cx, ty, subpatterns) } }