From c9b33ee4bc86b8338d36968ac1a088aff9b036a2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 21 Sep 2019 13:49:14 +0200 Subject: [PATCH 01/87] Run `rustfmt` --- src/librustc_mir/hair/pattern/_match.rs | 708 +++++++++---------- src/librustc_mir/hair/pattern/check_match.rs | 232 +++--- 2 files changed, 467 insertions(+), 473 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 3ea5805287724..9b18fd794649e 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,16 +162,16 @@ 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; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable, Const}; -use rustc::ty::layout::{Integer, IntegerExt, VariantIdx, Size}; +use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; +use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable}; +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 syntax::attr::{SignedInt, UnsignedInt}; @@ -180,13 +179,13 @@ 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>) -> &'a Pat<'tcx> { cx.pattern_arena.alloc(LiteralExpander { tcx: cx.tcx }.fold_pattern(&pat)) @@ -215,11 +214,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); @@ -228,12 +224,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), } @@ -246,30 +241,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), } } } @@ -277,9 +269,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, } } } @@ -315,15 +306,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); @@ -344,7 +334,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()) } @@ -453,7 +444,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), } } } @@ -462,14 +453,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, } } } @@ -477,7 +468,7 @@ impl<'tcx> Usefulness<'tcx> { #[derive(Copy, Clone, Debug)] pub enum WitnessPreference { ConstructWitness, - LeaveOutWitness + LeaveOutWitness, } #[derive(Copy, Clone, Debug)] @@ -531,16 +522,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) } @@ -560,25 +548,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() { @@ -586,7 +570,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 } @@ -596,37 +580,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 } @@ -646,48 +618,45 @@ fn all_constructors<'a, 'tcx>( 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)) - }).collect() + [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).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. - ConstantRange('\u{0000}' as u128, - '\u{D7FF}' as u128, - cx.tcx.types.char, - RangeEnd::Included + ConstantRange( + '\u{0000}' as u128, + '\u{D7FF}' as u128, + cx.tcx.types.char, + RangeEnd::Included, ), - ConstantRange('\u{E000}' as u128, - '\u{10FFFF}' as u128, - cx.tcx.types.char, - RangeEnd::Included + ConstantRange( + '\u{E000}' as u128, + '\u{10FFFF}' as u128, + cx.tcx.types.char, + RangeEnd::Included, ), ] } @@ -791,15 +760,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 } => { @@ -874,7 +841,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 }) @@ -943,7 +910,7 @@ impl<'tcx> IntRange<'tcx> { } box PatKind::AscribeUserType { ref subpattern, .. } => { pat = subpattern; - }, + } _ => return None, } } @@ -956,7 +923,7 @@ impl<'tcx> IntRange<'tcx> { let bits = Integer::from_attr(&tcx, SignedInt(ity)).size().bits() as u128; 1u128 << (bits - 1) } - _ => 0 + _ => 0, } } @@ -984,15 +951,15 @@ 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)); @@ -1144,7 +1111,7 @@ pub fn is_useful<'p, 'a, 'tcx>( } } else { NotUseful - } + }; }; assert!(rows.iter().all(|r| r.len() == v.len())); @@ -1170,31 +1137,34 @@ pub fn is_useful<'p, 'a, 'tcx>( // introducing uninhabited patterns for inaccessible fields. We // need to figure out how to model that. ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()).unwrap_or(v[0].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[0]).chain(Some(v[0]))), }; debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v[0]); if let Some(constructors) = pat_constructors(cx, v[0], pcx) { let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty); - debug!("is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}", - constructors, is_declared_nonexhaustive); + debug!( + "is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}", + constructors, is_declared_nonexhaustive + ); if is_declared_nonexhaustive { Useful } else { - split_grouped_constructors( - cx.tcx, cx.param_env, constructors, matrix, pcx.ty, - ).into_iter().map(|c| - is_useful_specialized(cx, matrix, v, c, pcx.ty, witness) - ).find(|result| result.is_useful()).unwrap_or(NotUseful) + split_grouped_constructors(cx.tcx, cx.param_env, constructors, matrix, pcx.ty) + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) + .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 `_`). @@ -1225,32 +1195,39 @@ 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) - .into_iter().map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) .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) { UsefulWithWitness(pats) => { let cx = &*cx; @@ -1301,35 +1278,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, } } } @@ -1347,29 +1332,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) { 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, } } @@ -1381,37 +1359,33 @@ fn is_useful_specialized<'p, 'a, 'tcx>( /// `[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>(cx: &mut MatchCheckCtxt<'_, 'tcx>, - pat: &Pat<'tcx>, - pcx: PatCtxt<'tcx>) - -> Option>> -{ +fn pat_constructors<'tcx>( + cx: &mut MatchCheckCtxt<'_, 'tcx>, + pat: &Pat<'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)]), - 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, - )]), + 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, + )]), 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)]) } @@ -1434,13 +1408,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, } } @@ -1459,37 +1431,43 @@ 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() { // Use T as the sub pattern type of Box. vec![substs.type_at(0)] } else { - adt.variants[ctor.variant_index_for_adt(cx, adt)].fields.iter().map(|field| { - let is_visible = adt.is_enum() - || field.vis.is_accessible_from(cx.module, cx.tcx); - if is_visible { - 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, + adt.variants[ctor.variant_index_for_adt(cx, adt)] + .fields + .iter() + .map(|field| { + let is_visible = + adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); + if is_visible { + 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, + } + } else { + // 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. + cx.tcx.types.err } - } else { - // 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. - cx.tcx.types.err - } - }).collect() + }) + .collect() } } _ => vec![], @@ -1514,17 +1492,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, ), }; @@ -1533,9 +1514,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 } => { @@ -1640,7 +1622,8 @@ fn split_grouped_constructors<'p, 'tcx>( // `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])) .flat_map(|range| ctor_range.intersection(&range)) .flat_map(|range| range_borders(range)); @@ -1651,8 +1634,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 }) @@ -1664,8 +1647,8 @@ fn split_grouped_constructors<'p, 'tcx>( Some(IntRange { range: n..=u128::MAX, ty }) } (Border::AfterMax, _) => None, - } - }) { + }) + { split_ctors.push(IntRange::range_to_ctor(tcx, ty, range)); } } @@ -1689,8 +1672,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) => { @@ -1703,37 +1687,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!(), @@ -1742,9 +1720,8 @@ fn constructor_covered_by_range<'tcx>( fn patterns_for_variant<'p, 'tcx>( subpatterns: &'p [FieldPat<'tcx>], - wild_patterns: &[&'p Pat<'tcx>]) - -> SmallVec<[&'p Pat<'tcx>; 2]> -{ + wild_patterns: &[&'p Pat<'tcx>], +) -> SmallVec<[&'p Pat<'tcx>; 2]> { let mut result = SmallVec::from_slice(wild_patterns); for subpat in subpatterns { @@ -1776,9 +1753,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]; @@ -1787,13 +1762,9 @@ fn specialize<'p, 'a: 'p, 'tcx>( .map(|_| patterns_for_variant(subpatterns, wild_patterns)) } - PatKind::Leaf { ref subpatterns } => { - Some(patterns_for_variant(subpatterns, wild_patterns)) - } + PatKind::Leaf { ref subpatterns } => Some(patterns_for_variant(subpatterns, wild_patterns)), - 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 @@ -1801,39 +1772,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 {:?}", @@ -1845,41 +1805,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 { @@ -1895,39 +1851,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."); @@ -1936,7 +1902,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 9bed4fb66ea9d..f23f07c56dbed 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -1,24 +1,24 @@ -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::def::*; use rustc::hir::def_id::DefId; -use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; +use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; 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) { @@ -99,13 +99,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(); } } } @@ -122,12 +124,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); @@ -140,30 +137,38 @@ 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 = expand_pattern(cx, patcx.lower_pattern(&pat)); - 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 = expand_pattern(cx, patcx.lower_pattern(&pat)); + 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 { @@ -189,17 +194,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 { @@ -207,18 +211,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)); } @@ -246,15 +257,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 pats: Matrix<'_, '_> = vec![smallvec![ - expand_pattern(cx, pattern) - ]].into_iter().collect(); + let pats: Matrix<'_, '_> = + vec![smallvec![expand_pattern(cx, pattern)]].into_iter().collect(); let witnesses = match check_not_useful(cx, pattern_ty, &pats) { Ok(_) => return, @@ -263,9 +272,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)) @@ -284,8 +296,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, @@ -294,8 +308,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); @@ -308,11 +324,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, @@ -339,19 +354,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(); } @@ -370,10 +392,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, } } @@ -392,8 +412,9 @@ fn check_arms<'tcx>( match is_useful(cx, &seen, &v, LeaveOutWitness) { 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, @@ -410,9 +431,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( @@ -421,13 +444,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, @@ -444,12 +466,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); @@ -491,14 +512,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(); } @@ -551,7 +573,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))); @@ -563,13 +585,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)); @@ -654,7 +678,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<'_, '_, '_> { @@ -666,10 +690,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 3ea9c9cf2acba981ae92fba1f64b4d5bf4abf378 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 26 Sep 2019 20:47:05 +0200 Subject: [PATCH 02/87] 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 9b18fd794649e..aa37a2a870190 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -290,17 +290,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")?; @@ -1356,7 +1356,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>( @@ -1736,7 +1736,7 @@ fn patterns_for_variant<'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 f9834608a91299ecce87d56baa751e1f7ff13bcd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 10:01:57 +0200 Subject: [PATCH 03/87] 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 | 182 ++++++++++++++++-------- 1 file changed, 120 insertions(+), 62 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index aa37a2a870190..364ac1e39558f 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,86 +38,140 @@ /// (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. +/// +/// /// 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 /// ------------------------------ @@ -150,7 +208,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 77e3a9518fdb25e18dd800afdc49c7f70ddf9d10 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 26 Sep 2019 20:47:15 +0200 Subject: [PATCH 04/87] Abstract out pattern stacks to make the code more legible --- src/librustc_mir/hair/pattern/_match.rs | 181 ++++++++++++++----- src/librustc_mir/hair/pattern/check_match.rs | 11 +- 2 files changed, 142 insertions(+), 50 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 364ac1e39558f..08d78235bf8b4 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -333,18 +333,114 @@ 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) + } + fn replace_head<'p2>( + &self, + f: impl FnOnce(&'p Pat<'tcx>) -> PatStack<'p2, 'tcx>, + ) -> PatStack<'p2, 'tcx> + where + 'p: 'p2, + { + let mut v = f(self.head()); + v.0.extend_from_slice(&self.0[1..]); + v + } + + /// This computes `D(self)`. See top of the file for explanations. + fn pop_wildcard(&self) -> Option { + if self.0[0].is_wildcard() { Some(self.to_tail()) } else { None } + } + /// This computes `S(constructor, self)`. See top of the file for explanations. + fn pop_constructor<'a, 'p2>( + &self, + cx: &mut MatchCheckCtxt<'a, 'tcx>, + constructor: &Constructor<'tcx>, + wild_patterns: &[&'p2 Pat<'tcx>], + ) -> Option> + where + 'a: 'p2, + 'p: 'p2, + { + specialize(cx, self, constructor, wild_patterns) + } +} + +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) } + + /// This computes `D(self)`. See top of the file for explanations. + fn pop_wildcard(&self) -> Self { + self.0.iter().filter_map(|r| r.pop_wildcard()).collect() + } + /// This computes `S(constructor, self)`. See top of the file for explanations. + fn pop_constructor<'a, 'p2>( + &self, + cx: &mut MatchCheckCtxt<'a, 'tcx>, + constructor: &Constructor<'tcx>, + wild_patterns: &[&'p2 Pat<'tcx>], + ) -> Matrix<'p2, 'tcx> + where + 'a: 'p2, + 'p: 'p2, + { + Matrix( + self.0.iter().flat_map(|r| r.pop_constructor(cx, constructor, wild_patterns)).collect(), + ) + } } /// Pretty-printer for matrices of patterns, example: @@ -390,10 +486,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()) } @@ -1150,7 +1246,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, ) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; @@ -1194,14 +1290,19 @@ pub fn is_useful<'p, 'a, 'tcx>( // FIXME: this might lead to "unstable" behavior with macro hygiene // introducing uninhabited patterns for inaccessible fields. We // need to figure out how to model that. - ty: rows.iter().map(|r| r[0].ty).find(|ty| !ty.references_error()).unwrap_or(v[0].ty), - max_slice_length: max_slice_length(cx, rows.iter().map(|r| r[0]).chain(Some(v[0]))), + ty: rows + .iter() + .map(|r| r.head().ty) + .find(|ty| !ty.references_error()) + .unwrap_or(v.head().ty), + max_slice_length: max_slice_length(cx, rows.iter().map(|r| r.head()).chain(Some(v.head()))), }; - 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) { - let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v[0]) && !cx.is_local(pcx.ty); + if let Some(constructors) = pat_constructors(cx, v.head(), pcx) { + let is_declared_nonexhaustive = + cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(pcx.ty); debug!( "is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}", constructors, is_declared_nonexhaustive @@ -1221,7 +1322,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 @@ -1280,13 +1381,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[0].is_wildcard() { Some(SmallVec::from_slice(&r[1..])) } else { None } - }) - .collect(); - match is_useful(cx, &matrix, &v[1..], witness) { + let matrix = matrix.pop_wildcard(); + let v = v.to_tail(); + match is_useful(cx, &matrix, &v, witness) { UsefulWithWitness(pats) => { let cx = &*cx; // In this case, there's at least one "free" @@ -1382,8 +1479,8 @@ 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>, - v: &[&Pat<'tcx>], + matrix: &Matrix<'p, 'tcx>, + v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, lty: Ty<'tcx>, witness: WitnessPreference, @@ -1393,9 +1490,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.pop_constructor(cx, &ctor, &wild_patterns); + match v.pop_constructor(cx, &ctor, &wild_patterns) { Some(v) => match is_useful(cx, &matrix, &v, witness) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses @@ -1682,7 +1778,7 @@ fn split_grouped_constructors<'p, 'tcx>( // class lies between 2 borders. let row_borders = m .iter() - .flat_map(|row| IntRange::from_pat(tcx, param_env, row[0])) + .flat_map(|row| IntRange::from_pat(tcx, param_env, row.head())) .flat_map(|range| ctor_range.intersection(&range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); @@ -1779,7 +1875,7 @@ fn constructor_covered_by_range<'tcx>( fn patterns_for_variant<'p, 'tcx>( subpatterns: &'p [FieldPat<'tcx>], wild_patterns: &[&'p Pat<'tcx>], -) -> SmallVec<[&'p Pat<'tcx>; 2]> { +) -> PatStack<'p, 'tcx> { let mut result = SmallVec::from_slice(wild_patterns); for subpat in subpatterns { @@ -1787,7 +1883,7 @@ fn patterns_for_variant<'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 @@ -1798,20 +1894,20 @@ fn patterns_for_variant<'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, 'p2: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - r: &[&'p Pat<'tcx>], + r: &PatStack<'p2, '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 { + let new_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]; @@ -1822,7 +1918,7 @@ fn specialize<'p, 'a: 'p, 'tcx>( PatKind::Leaf { ref subpatterns } => Some(patterns_for_variant(subpatterns, wild_patterns)), - 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 @@ -1892,7 +1988,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, } @@ -1903,7 +1999,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, } } @@ -1945,7 +2041,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, } @@ -1957,10 +2053,7 @@ 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, new_head); - head.map(|mut head| { - head.extend_from_slice(&r[1..]); - head - }) + new_head.map(|new_head| r.replace_head(|_| new_head)) } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index f23f07c56dbed..048f9fa11e1c1 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}; @@ -15,7 +15,6 @@ use rustc::hir::def_id::DefId; use rustc::hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc::hir::{self, Pat}; -use smallvec::smallvec; use std::slice; use syntax_pos::{MultiSpan, Span, DUMMY_SP}; @@ -247,7 +246,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); @@ -263,7 +262,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; let pats: Matrix<'_, '_> = - vec![smallvec![expand_pattern(cx, pattern)]].into_iter().collect(); + vec![PatStack::from_pattern(expand_pattern(cx, pattern))].into_iter().collect(); let witnesses = match check_not_useful(cx, pattern_ty, &pats) { Ok(_) => return, @@ -407,7 +406,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) { NotUseful => { @@ -488,7 +487,7 @@ fn check_not_useful( matrix: &Matrix<'_, 'tcx>, ) -> Result<(), Vec>> { let wild_pattern = super::Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; - match is_useful(cx, matrix, &[&wild_pattern], ConstructWitness) { + match is_useful(cx, matrix, &PatStack::from_pattern(&wild_pattern), ConstructWitness) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { vec![wild_pattern] From e1b7627383d8b3c06ebaf654fbe4198cec39bd8b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 16:07:23 +0200 Subject: [PATCH 05/87] Remove some redundancy --- src/librustc_mir/hair/pattern/_match.rs | 111 ++++++++++++++---------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 08d78235bf8b4..f7c2da4787890 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -241,7 +241,7 @@ 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::iter::{self, FromIterator, IntoIterator}; use std::ops::RangeInclusive; use std::u128; @@ -355,7 +355,10 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { fn len(&self) -> usize { self.0.len() } - fn head(&self) -> &'p Pat<'tcx> { + fn head<'a, 'p2>(&'a self) -> &'p2 Pat<'tcx> + where + 'p: 'p2, + { self.0[0] } fn to_tail(&self) -> Self { @@ -422,6 +425,21 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.0.push(row) } + /// Iterate over the first component of each row + // Can't return impl Iterator because of hidden lifetime capture. + fn heads<'a, 'p2>( + &'a self, + ) -> iter::Map< + std::slice::Iter<'a, PatStack<'p, 'tcx>>, + impl FnMut(&'a PatStack<'p, 'tcx>) -> &'p2 Pat<'tcx>, + > + where + 'p: 'p2, + 'a: 'p2, + { + self.0.iter().map(|r| r.head()) + } + /// This computes `D(self)`. See top of the file for explanations. fn pop_wildcard(&self) -> Self { self.0.iter().filter_map(|r| r.pop_wildcard()).collect() @@ -601,6 +619,39 @@ impl<'tcx> Constructor<'tcx> { _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } + + 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)] @@ -678,12 +729,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) } @@ -706,7 +752,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(); @@ -1290,12 +1336,8 @@ pub fn is_useful<'p, 'a, 'tcx>( // FIXME: this might lead to "unstable" behavior with macro hygiene // introducing uninhabited patterns for inaccessible fields. We // need to figure out how to model that. - ty: rows - .iter() - .map(|r| r.head().ty) - .find(|ty| !ty.references_error()) - .unwrap_or(v.head().ty), - max_slice_length: max_slice_length(cx, rows.iter().map(|r| r.head()).chain(Some(v.head()))), + ty: matrix.heads().map(|p| p.ty).find(|ty| !ty.references_error()).unwrap_or(v.head().ty), + max_slice_length: max_slice_length(cx, matrix.heads().chain(Some(v.head()))), }; debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); @@ -1320,10 +1362,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 `_`). @@ -1486,9 +1526,8 @@ fn is_useful_specialized<'p, 'a, 'tcx>( witness: WitnessPreference, ) -> 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.pop_constructor(cx, &ctor, &wild_patterns); match v.pop_constructor(cx, &ctor, &wild_patterns) { @@ -1550,26 +1589,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. /// @@ -1739,7 +1758,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>, ) -> Vec> { let mut split_ctors = Vec::with_capacity(ctors.len()); @@ -1776,9 +1795,9 @@ fn split_grouped_constructors<'p, 'tcx>( // `borders` is the set of borders between equivalence classes: each equivalence // class lies between 2 borders. - let row_borders = m - .iter() - .flat_map(|row| IntRange::from_pat(tcx, param_env, row.head())) + let row_borders = matrix + .heads() + .flat_map(|pat| IntRange::from_pat(tcx, param_env, pat)) .flat_map(|range| ctor_range.intersection(&range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); From a075db89a435e6ceea58e595cd814fc9ed70b1ae Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 16:37:01 +0200 Subject: [PATCH 06/87] 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 f7c2da4787890..643ba609f737b 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1191,41 +1191,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 { @@ -1239,32 +1215,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. @@ -1390,22 +1353,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.is_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" @@ -1414,7 +1374,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, all_ctors, matrix, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) @@ -1484,28 +1445,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 7ee9cb7a5b996829cf49f10a70698937c3f88ef7 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:36:42 +0200 Subject: [PATCH 07/87] 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 643ba609f737b..6dd65f7432cfe 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -652,6 +652,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)] @@ -755,50 +816,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 a150d972fe2c32718e6fdd7695e85c7b9a1546a9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:37:42 +0200 Subject: [PATCH 08/87] 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 6dd65f7432cfe..355d36e4f7952 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -620,15 +620,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 @@ -713,6 +715,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)] @@ -784,16 +792,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. /// @@ -1453,33 +1451,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, @@ -1500,7 +1493,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.pop_constructor(cx, &ctor, &wild_patterns); match v.pop_constructor(cx, &ctor, &wild_patterns) { From f0e8c78c01c70186f6c962e15f564fe5049f0333 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:44:24 +0200 Subject: [PATCH 09/87] Clarify some variable names --- src/librustc_mir/hair/pattern/_match.rs | 68 ++++++++++++++----------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 355d36e4f7952..cb604fd74facf 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -388,13 +388,13 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { &self, cx: &mut MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, - wild_patterns: &[&'p2 Pat<'tcx>], + ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], ) -> Option> where 'a: 'p2, 'p: 'p2, { - specialize(cx, self, constructor, wild_patterns) + specialize(cx, self, constructor, ctor_wild_subpatterns) } } @@ -449,14 +449,17 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { &self, cx: &mut MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, - wild_patterns: &[&'p2 Pat<'tcx>], + ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], ) -> Matrix<'p2, 'tcx> where 'a: 'p2, 'p: 'p2, { Matrix( - self.0.iter().flat_map(|r| r.pop_constructor(cx, constructor, wild_patterns)).collect(), + self.0 + .iter() + .flat_map(|r| r.pop_constructor(cx, constructor, ctor_wild_subpatterns)) + .collect(), ) } } @@ -1276,7 +1279,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, ) -> Usefulness<'tcx> { let &Matrix(ref rows) = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); @@ -1288,7 +1291,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, } @@ -1338,7 +1341,7 @@ pub fn is_useful<'p, 'a, 'tcx>( } else { split_grouped_constructors(cx.tcx, cx.param_env, constructors, matrix, pcx.ty) .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } @@ -1398,14 +1401,14 @@ pub fn is_useful<'p, 'a, 'tcx>( drop(missing_ctors); // It was borrowing `all_ctors`, which we want to move. split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, matrix, pcx.ty) .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness)) + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { let matrix = matrix.pop_wildcard(); let v = v.to_tail(); - match is_useful(cx, &matrix, &v, witness) { - UsefulWithWitness(pats) => { + match is_useful(cx, &matrix, &v, witness_preference) { + UsefulWithWitness(witnesses) => { let cx = &*cx; // In this case, there's at least one "free" // constructor that is only matched against by @@ -1463,7 +1466,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| { @@ -1489,15 +1492,15 @@ fn is_useful_specialized<'p, 'a, 'tcx>( v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, lty: Ty<'tcx>, - witness: WitnessPreference, + witness_preference: WitnessPreference, ) -> 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.pop_constructor(cx, &ctor, &wild_patterns); - match v.pop_constructor(cx, &ctor, &wild_patterns) { - Some(v) => match is_useful(cx, &matrix, &v, witness) { + 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.pop_constructor(cx, &ctor, &ctor_wild_subpatterns); + match v.pop_constructor(cx, &ctor, &ctor_wild_subpatterns) { + Some(v) => match is_useful(cx, &matrix, &v, witness_preference) { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses .into_iter() @@ -1859,15 +1862,18 @@ fn constructor_covered_by_range<'tcx>( fn patterns_for_variant<'p, 'tcx>( subpatterns: &'p [FieldPat<'tcx>], - wild_patterns: &[&'p Pat<'tcx>], + ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> PatStack<'p, 'tcx> { - let mut result = SmallVec::from_slice(wild_patterns); + let mut result = SmallVec::from_slice(ctor_wild_subpatterns); for subpat in subpatterns { result[subpat.field.index()] = &subpat.pattern; } - debug!("patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, wild_patterns, result); + debug!( + "patterns_for_variant({:#?}, {:#?}) = {:#?}", + subpatterns, ctor_wild_subpatterns, result + ); PatStack::from_vec(result) } @@ -1883,25 +1889,29 @@ fn specialize<'p, 'a: 'p, 'p2: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, r: &PatStack<'p2, '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]; Some(Variant(variant.def_id)) .filter(|variant_constructor| variant_constructor == constructor) - .map(|_| patterns_for_variant(subpatterns, wild_patterns)) + .map(|_| patterns_for_variant(subpatterns, ctor_wild_subpatterns)) } - PatKind::Leaf { ref subpatterns } => Some(patterns_for_variant(subpatterns, wild_patterns)), + PatKind::Leaf { ref subpatterns } => { + Some(patterns_for_variant(subpatterns, ctor_wild_subpatterns)) + } PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)), @@ -1940,7 +1950,7 @@ fn specialize<'p, 'a: 'p, 'p2: '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); @@ -1994,13 +2004,13 @@ fn specialize<'p, 'a: 'p, 'p2: '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()) @@ -2038,7 +2048,7 @@ fn specialize<'p, 'a: 'p, 'p2: '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(|new_head| r.replace_head(|_| new_head)) } From 8a2274bae47016f085918a9c207616577417d04c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 17:58:41 +0200 Subject: [PATCH 10/87] `specialize` conceptually operates on a single pattern --- src/librustc_mir/hair/pattern/_match.rs | 37 ++++++++++--------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index cb604fd74facf..f3dc564ca408c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -367,17 +367,6 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { fn iter(&self) -> impl Iterator> { self.0.iter().map(|p| *p) } - fn replace_head<'p2>( - &self, - f: impl FnOnce(&'p Pat<'tcx>) -> PatStack<'p2, 'tcx>, - ) -> PatStack<'p2, 'tcx> - where - 'p: 'p2, - { - let mut v = f(self.head()); - v.0.extend_from_slice(&self.0[1..]); - v - } /// This computes `D(self)`. See top of the file for explanations. fn pop_wildcard(&self) -> Option { @@ -394,7 +383,11 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { 'a: 'p2, 'p: 'p2, { - 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 + }) } } @@ -1877,26 +1870,24 @@ fn patterns_for_variant<'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, 'p2: 'p, 'tcx>( +fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - r: &PatStack<'p2, 'tcx>, + pat: &'p2 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) + .pop_constructor(cx, constructor, ctor_wild_subpatterns), PatKind::Binding { .. } | PatKind::Wild => { Some(PatStack::from_slice(ctor_wild_subpatterns)) @@ -2048,7 +2039,7 @@ fn specialize<'p, 'a: 'p, 'p2: '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(|new_head| r.replace_head(|_| new_head)) + result } From 0f5e02e905c2e65b506228873a1e1c539aa9908c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 19:09:30 +0200 Subject: [PATCH 11/87] Wording --- src/librustc_mir/hair/pattern/_match.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f3dc564ca408c..b82cfd9868d90 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1424,7 +1424,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 { @@ -1766,7 +1766,7 @@ fn split_grouped_constructors<'p, 'tcx>( let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); borders.sort_unstable(); - // We're going to iterate through every pair of borders, making sure that each + // 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 From c95b7ae7d8c0741f894f1a20cd6e5a63cf6481e2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 20:41:38 +0200 Subject: [PATCH 12/87] Add some slice-pattern exhaustiveness tests --- .../pattern/slice-pattern-exhaustiveness.rs | 28 +++++++++++++++++++ .../slice-pattern-exhaustiveness.stderr | 11 ++++++++ 2 files changed, 39 insertions(+) create mode 100644 src/test/ui/pattern/slice-pattern-exhaustiveness.rs create mode 100644 src/test/ui/pattern/slice-pattern-exhaustiveness.stderr diff --git a/src/test/ui/pattern/slice-pattern-exhaustiveness.rs b/src/test/ui/pattern/slice-pattern-exhaustiveness.rs new file mode 100644 index 0000000000000..681e01cdb0554 --- /dev/null +++ b/src/test/ui/pattern/slice-pattern-exhaustiveness.rs @@ -0,0 +1,28 @@ +#![feature(slice_patterns)] + +fn main() { + let s: [bool; 1] = [false; 1]; + match s { + [a] => {} + } + match s { + [a, ..] => {} + } + match s { + [true, ..] => {} + [.., false] => {} + } + + let s: [bool; 2] = [false; 2]; + match s { + [a, b] => {} + } + match s { + [a, ..] => {} + } + match s { + //~^ ERROR `[false, true]` not covered + [true, ..] => {} + [.., false] => {} + } +} diff --git a/src/test/ui/pattern/slice-pattern-exhaustiveness.stderr b/src/test/ui/pattern/slice-pattern-exhaustiveness.stderr new file mode 100644 index 0000000000000..ae9695e521499 --- /dev/null +++ b/src/test/ui/pattern/slice-pattern-exhaustiveness.stderr @@ -0,0 +1,11 @@ +error[E0004]: non-exhaustive patterns: `[false, true]` not covered + --> $DIR/slice-pattern-exhaustiveness.rs:23:11 + | +LL | match s { + | ^ pattern `[false, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0004`. From ea3fe4ec279a800e67c5349722cf96352ad29b8a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 23 Sep 2019 21:24:58 +0200 Subject: [PATCH 13/87] Rename Constructor::Slice to FixedLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index b82cfd9868d90..c149cb0c0e6e8 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -589,13 +589,13 @@ enum Constructor<'tcx> { /// Ranges of literal values (`2..=5` and `2..5`). ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), /// Array patterns of length n. - Slice(u64), + FixedLenSlice(u64), } impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { - Slice { .. } => true, + FixedLenSlice { .. } => true, _ => false, } } @@ -639,7 +639,7 @@ impl<'tcx> Constructor<'tcx> { match ty.kind { ty::Tuple(ref fs) => fs.len() as u64, ty::Slice(..) | ty::Array(..) => match *self { - Slice(length) => length, + FixedLenSlice(length) => length, ConstantValue(_) => 0, _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, @@ -838,14 +838,14 @@ fn all_constructors<'a, 'tcx>( } ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => { let len = len.eval_usize(cx.tcx, cx.param_env); - if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![Slice(len)] } + if len != 0 && cx.is_uninhabited(sub_ty) { vec![] } else { vec![FixedLenSlice(len)] } } // Treat arrays of a constant but unknown length like slices. ty::Array(ref sub_ty, _) | ty::Slice(ref sub_ty) => { if cx.is_uninhabited(sub_ty) { - vec![Slice(0)] + vec![FixedLenSlice(0)] } else { - (0..pcx.max_slice_length + 1).map(|length| Slice(length)).collect() + (0..pcx.max_slice_length + 1).map(|length| FixedLenSlice(length)).collect() } } ty::Adt(def, substs) if def.is_enum() => def @@ -1534,15 +1534,17 @@ fn pat_constructors<'tcx>( end, )]), PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => Some(vec![Slice(length.eval_usize(cx.tcx, cx.param_env))]), + ty::Array(_, length) => { + Some(vec![FixedLenSlice(length.eval_usize(cx.tcx, cx.param_env))]) + } _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let pat_len = prefix.len() as u64 + suffix.len() as u64; if slice.is_some() { - Some((pat_len..pcx.max_slice_length + 1).map(Slice).collect()) + Some((pat_len..pcx.max_slice_length + 1).map(FixedLenSlice).collect()) } else { - Some(vec![Slice(pat_len)]) + Some(vec![FixedLenSlice(pat_len)]) } } PatKind::Or { .. } => { @@ -1564,7 +1566,7 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( match ty.kind { ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), ty::Slice(ty) | ty::Array(ty, _) => match *ctor { - Slice(length) => (0..length).map(|_| ty).collect(), + FixedLenSlice(length) => (0..length).map(|_| ty).collect(), ConstantValue(_) => vec![], _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), }, @@ -1993,7 +1995,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( PatKind::Array { ref prefix, ref slice, ref suffix } | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { - Slice(..) => { + FixedLenSlice(..) => { let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { From d4cc125ea12299c3d474ef3b3b7b8e2a35434af0 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 26 Sep 2019 22:34:27 +0200 Subject: [PATCH 14/87] IntRange::from_pat is redundant with pat_constructors --- src/librustc_mir/hair/pattern/_match.rs | 31 ++++++++++++++----------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index c149cb0c0e6e8..036b70716d78e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1321,7 +1321,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - if let Some(constructors) = pat_constructors(cx, v.head(), pcx) { + if let Some(constructors) = pat_constructors(cx.tcx, cx.param_env, v.head(), pcx) { let is_declared_nonexhaustive = cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(pcx.ty); debug!( @@ -1332,7 +1332,7 @@ pub fn is_useful<'p, 'a, 'tcx>( if is_declared_nonexhaustive { Useful } else { - split_grouped_constructors(cx.tcx, cx.param_env, constructors, matrix, pcx.ty) + split_grouped_constructors(cx.tcx, cx.param_env, pcx, constructors, matrix, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) @@ -1341,8 +1341,10 @@ pub fn is_useful<'p, 'a, 'tcx>( } else { debug!("is_useful - expanding wildcard"); - let used_ctors: Vec> = - matrix.heads().flat_map(|p| pat_constructors(cx, p, pcx).unwrap_or(vec![])).collect(); + let used_ctors: Vec> = matrix + .heads() + .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, 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 `_`). @@ -1392,7 +1394,7 @@ pub fn is_useful<'p, 'a, 'tcx>( 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, all_ctors, matrix, pcx.ty) + split_grouped_constructors(cx.tcx, cx.param_env, pcx, all_ctors, matrix, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) @@ -1515,12 +1517,15 @@ fn is_useful_specialized<'p, 'a, 'tcx>( /// /// Returns `None` in case of a catch-all, which can't be specialized. fn pat_constructors<'tcx>( - cx: &mut MatchCheckCtxt<'_, 'tcx>, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, pcx: PatCtxt<'tcx>, ) -> Option>> { match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => pat_constructors(cx, subpattern, pcx), + PatKind::AscribeUserType { ref subpattern, .. } => { + pat_constructors(tcx, param_env, subpattern, pcx) + } PatKind::Binding { .. } | PatKind::Wild => None, PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(vec![Single]), PatKind::Variant { adt_def, variant_index, .. } => { @@ -1528,15 +1533,13 @@ fn pat_constructors<'tcx>( } PatKind::Constant { value } => Some(vec![ConstantValue(value)]), 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.eval_bits(tcx, param_env, lo.ty), + hi.eval_bits(tcx, param_env, hi.ty), lo.ty, end, )]), PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => { - Some(vec![FixedLenSlice(length.eval_usize(cx.tcx, cx.param_env))]) - } + ty::Array(_, length) => Some(vec![FixedLenSlice(length.eval_usize(tcx, param_env))]), _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { @@ -1721,6 +1724,7 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) fn split_grouped_constructors<'p, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, + pcx: PatCtxt<'tcx>, ctors: Vec>, matrix: &Matrix<'p, 'tcx>, ty: Ty<'tcx>, @@ -1761,7 +1765,8 @@ fn split_grouped_constructors<'p, 'tcx>( // class lies between 2 borders. let row_borders = matrix .heads() - .flat_map(|pat| IntRange::from_pat(tcx, param_env, pat)) + .flat_map(|pat| pat_constructors(tcx, param_env, pat, pcx).unwrap_or(vec![])) + .flat_map(|ctor| IntRange::from_ctor(tcx, param_env, &ctor)) .flat_map(|range| ctor_range.intersection(&range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); From 501cf3a66903beb712df7ddb76e895620f210b6d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 26 Sep 2019 22:41:04 +0200 Subject: [PATCH 15/87] split_grouped_constructors only needs access to the list of used constructors --- src/librustc_mir/hair/pattern/_match.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 036b70716d78e..14ce48a1bea34 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1332,7 +1332,11 @@ pub fn is_useful<'p, 'a, 'tcx>( if is_declared_nonexhaustive { Useful } else { - split_grouped_constructors(cx.tcx, cx.param_env, pcx, constructors, matrix, pcx.ty) + let used_ctors: Vec> = matrix + .heads() + .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, pcx).unwrap_or(vec![])) + .collect(); + split_grouped_constructors(cx.tcx, cx.param_env, constructors, &used_ctors, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) @@ -1394,7 +1398,7 @@ pub fn is_useful<'p, 'a, 'tcx>( 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, pcx, all_ctors, matrix, pcx.ty) + split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, &used_ctors, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) @@ -1721,17 +1725,16 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) /// boundaries for each interval range, sort them, then create constructors for each new interval /// between every pair of boundary points. (This essentially sums up to performing the intuitive /// merging operation depicted above.) -fn split_grouped_constructors<'p, 'tcx>( +fn split_grouped_constructors<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - pcx: PatCtxt<'tcx>, ctors: Vec>, - matrix: &Matrix<'p, 'tcx>, + matrix_ctors: &Vec>, ty: Ty<'tcx>, ) -> Vec> { let mut split_ctors = Vec::with_capacity(ctors.len()); - for ctor in ctors.into_iter() { + for ctor in ctors { match ctor { // For now, only ranges may denote groups of "subconstructors", so we only need to // special-case constant ranges. @@ -1763,10 +1766,9 @@ fn split_grouped_constructors<'p, 'tcx>( // `borders` is the set of borders between equivalence classes: each equivalence // class lies between 2 borders. - let row_borders = matrix - .heads() - .flat_map(|pat| pat_constructors(tcx, param_env, pat, pcx).unwrap_or(vec![])) - .flat_map(|ctor| IntRange::from_ctor(tcx, param_env, &ctor)) + let row_borders = matrix_ctors + .iter() + .flat_map(|ctor| IntRange::from_ctor(tcx, param_env, ctor)) .flat_map(|range| ctor_range.intersection(&range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); From b2ae93c9ed9afe715c621fc3b7503e11fdd29ba2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 26 Sep 2019 22:48:40 +0200 Subject: [PATCH 16/87] Share computation of used_ctors --- src/librustc_mir/hair/pattern/_match.rs | 54 ++++++++++++------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 14ce48a1bea34..f6bcf2f21af49 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1321,35 +1321,35 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); - if let Some(constructors) = pat_constructors(cx.tcx, cx.param_env, v.head(), pcx) { - let is_declared_nonexhaustive = - cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(pcx.ty); - debug!( - "is_useful - expanding constructors: {:#?}, is_declared_nonexhaustive: {:?}", - constructors, is_declared_nonexhaustive - ); + let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head(), pcx); - if is_declared_nonexhaustive { - Useful + let is_declared_nonexhaustive = !cx.is_local(pcx.ty) + && if v_constructors.is_some() { + cx.is_non_exhaustive_variant(v.head()) } else { - let used_ctors: Vec> = matrix - .heads() - .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, pcx).unwrap_or(vec![])) - .collect(); - split_grouped_constructors(cx.tcx, cx.param_env, constructors, &used_ctors, pcx.ty) - .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } + cx.is_non_exhaustive_enum(pcx.ty) + }; + debug!("is_useful - is_declared_nonexhaustive: {:?}", is_declared_nonexhaustive); + if v_constructors.is_some() && is_declared_nonexhaustive { + return Useful; + } + + let used_ctors: Vec> = matrix + .heads() + .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, pcx).unwrap_or(vec![])) + .collect(); + debug!("used_ctors = {:#?}", used_ctors); + + if let Some(constructors) = v_constructors { + debug!("is_useful - expanding constructors: {:#?}", constructors); + split_grouped_constructors(cx.tcx, cx.param_env, constructors, &used_ctors, pcx.ty) + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) } else { debug!("is_useful - expanding wildcard"); - let used_ctors: Vec> = matrix - .heads() - .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, 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 `_`). let all_ctors = all_constructors(cx, pcx); @@ -1382,12 +1382,10 @@ pub fn is_useful<'p, 'a, 'tcx>( 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!( - "missing_ctors.is_empty()={:#?} is_privately_empty={:#?} is_declared_nonexhaustive={:#?}", + "missing_ctors.is_empty()={:#?} is_privately_empty={:#?}", 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" @@ -1397,7 +1395,7 @@ pub fn is_useful<'p, 'a, 'tcx>( || (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. + drop(missing_ctors); // It was borrowing `all_ctors`, which we want to move out of. split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, &used_ctors, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) From 89df721fa732b16d7bc521008f2daadc8bb54755 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 17:07:14 +0200 Subject: [PATCH 17/87] Rework the exhaustiveness algorithm to better integrate meta-constructors This is strictly a refactoring: the new algorithm does exactly the same work as the previous one. However, it replaces the ad-hoc handling of ranges and slice patterns with a unified approach, using meta-constructors. This should improve both code legibility and future extensibility. --- src/librustc_mir/hair/pattern/_match.rs | 320 +++++++++++------------- 1 file changed, 149 insertions(+), 171 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f6bcf2f21af49..1776c33a7aaf6 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1,35 +1,111 @@ +/// # Introduction +/// /// This file includes the logic for exhaustiveness and usefulness checking for /// pattern-matching. Specifically, given a list of patterns for a type, we can /// tell whether: /// (a) the patterns cover every possible constructor for the type [exhaustiveness] /// (b) each pattern is necessary [usefulness] /// -/// The algorithm implemented here is a modified version of the one described in: +/// The algorithm implemented here is based on the one described in: /// http://moscova.inria.fr/~maranget/papers/warn/index.html -/// However, to save future implementors from reading the original paper, we -/// summarise the algorithm here to hopefully save time and be a little clearer -/// (without being so rigorous). +/// However, various modifications have been made to it so we keep it only as reference +/// and will describe the extended algorithm here (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)` 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- +/// are trying to compute a predicate `U(P, q)` where `P` is a list of patterns. +/// `U(P, q)` represents whether, given an existing list of patterns +/// `P_1 ..= P_m`, adding a new pattern `q` 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 -/// pattern to those that have come before it doesn't increase the number of values -/// we're matching). +/// (b) a pattern `P_i` is not useful (i.e. unreachable) if `U(P[0..=(i-1), P_i)` is +/// false (i.e., adding a pattern to those that have come before it doesn't match any value +/// that wasn't matched previously). +/// +/// +/// # Pattern-stacks and matrices +/// +/// The basic datastructure that we will make use of in the algorithm is a list of patterns that +/// the paper calls "pattern-vector" and that we call "pattern-stack". The idea is that we +/// start with a single pattern of interest, +/// and repeatedly unpack the top constructor to reveal its arguments. We keep the yet-untreated +/// arguments in the tail of the stack. +/// +/// For example, say we start with the pattern `Foo(Bar(1, 2), Some(true), false)`. The +/// pattern-stack might then evolve as follows: +/// [Foo(Bar(1, 2), Some(_), false)] // Initially we have a single pattern in the stack +/// [Bar(1, 2), Some(_), false] // After unpacking the `Foo` constructor +/// [1, 2, Some(_), false] // After unpacking the `Bar` constructor +/// [2, Some(_), false] // After unpacking the `1` constructor +/// // etc. +/// +/// We call the operation of popping the constructor on top of the stack "specialization", and we +/// write it `S(c, p)`, where `p` is a pattern-stack and `c` a specific constructor (like `Some` +/// or `None`). This operation returns zero or more altered pattern-stacks, as follows. +/// We look at the pattern `p_1` on top of the stack, and we have four cases: +/// 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 +/// 2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return nothing. +/// 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 +/// 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)) +/// +/// Note that when the required constructor does not match the constructor on top of the stack, we +/// return nothing. Thus specialization filters pattern-stacks by the constructor on top of them. +/// +/// We call a list of pattern-stacks a "matrix", because in the run of the algorithm they will +/// keep a rectangular shape. `S` operation extends straightforwardly to matrices by +/// working row-by-row using flat_map. +/// +/// +/// # Abstract algorithm +/// +/// The algorithm itself is a function `U`, that takes as arguments a matrix `M` and a new pattern +/// `p`, both with the same number `n` of columns. +/// The algorithm is inductive (on the number of columns: i.e., components of pattern-stacks). +/// The algorithm is realised in the `is_useful` function. +/// +/// Base case. (`n = 0`, i.e., an empty tuple pattern) +/// - If `M` already contains an empty pattern (i.e., if the number of patterns `m > 0`), +/// then `U(M, p)` is false. +/// - Otherwise, `M` must be empty, so `U(M, p)` is true. +/// +/// Inductive step. (`n > 0`) +/// We look at `p_1`, the head of the pattern-stack `p`. /// -/// 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`. +/// We first generate the list of constructors that are covered by a pattern `pat`. We name this operation +/// `pat_constructors`. +/// - If `pat == c(r_1, .., r_a)`, i.e. we have a constructor pattern. Then we just return `c`: +/// `pat_constructors(pat) = [c]` /// -/// For example, say we have the following: +/// - If `pat == _`, then we return the list of all possible constructors for the relevant type: +/// `pat_constructors(pat) = all_constructors(pat.ty)` +/// +/// - If `pat == r_1 | r_2`, then we return the constructors for either branch of the OR-pattern: +/// `pat_constructors(pat) = pat_constructors(r_1) + pat_constructors(r_2)` +/// +/// Then for each constructor `c` in `pat_constructors(p_1)`, we want to check whether a value +/// that starts with this constructor may show that `p` is useful, i.e. may match `p` but not +/// be matched by the matrix above. +/// For that, we only care about those rows of `M` whose first component covers the +/// constructor `c`; and for those rows that do, we want to unpack the arguments to `c` to check +/// further that `p` matches additional values. +/// This is where specialization comes in: this check amounts to computing `U(S(c, M), S(c, p))`. +/// More details can be found in the paper. +/// +/// Thus we get: `U(M, p) := ∃(c ϵ pat_constructors(p_1)) U(S(c, M), S(c, p))` +/// +/// Note that for c ϵ pat_constructors(p_1), `S(c, P)` always returns exactly one element, so the +/// formula above makes sense. +/// +/// TODO: example run of the algorithm /// ``` /// // x: (Option, Result<()>) /// match x { @@ -38,181 +114,83 @@ /// (None, Err(_)) => {} /// } /// ``` -/// Here, the matrix `P` starts as: +/// Here, the matrix `M` starts as: /// [ /// [(Some(true), _)], /// [(None, Err(()))], /// [(None, Err(_))], /// ] -/// We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering +/// We can tell it's not exhaustive, because `U(M, _)` is true (we're not covering /// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because /// all the values it covers are already covered by row 2. /// -/// 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. /// -/// 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)) +/// This algorithm however has a lot of practical issues. Most importantly, it may not terminate +/// in the presence of recursive types, since we always unpack all constructors as much +/// as possible. And it would be stupidly slow anyways for types with a lot of constructors, +/// like `u64` of `&[bool]`. /// -/// 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. 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. -/// -/// -/// 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-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)` 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 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`. +/// # Concrete algorithm /// -/// 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])`. +/// To make the algorithm tractable, we introduce the notion of meta-constructors. A +/// meta-constructor stands for a particular group of constructors. The typical example +/// is the wildcard `_`, which stands for all the constructors of a given type. /// -/// - 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))` +/// In practice, the meta-constructors we make use of in this file are the following: +/// - any normal constructor is also a metaconstructor with exactly one member; +/// - the wildcard `_`, that captures all constructors of a given type; +/// - the constant range `x..y` that captures a range of values for types that support +/// it, like integers; +/// - the variable-length slice `[x, y, .., z]`, that captures all slice constructors +/// from a given length onwards; +/// - the "missing constructors" metaconstructor, that captures a provided arbitrary group +/// of constructors. /// -/// 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])`. +/// We first redefine `pat_constructors` to potentially return a metaconstructor when relevant +/// for a pattern. /// -/// + 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))` +/// We then add a step to the algorithm: a function `split_metaconstructor(mc, M)` that returns +/// a list of metaconstructors, with the following properties: +/// - the set of base constructors covered by the output must be the same as covered by `mc`; +/// - for each metaconstructor `k` in the output, all the `c ϵ k` behave the same relative +/// to `M`. More precisely, we want that for any two `c1` and `c2` in `k`, +/// `U(S(c1, M), S(c1, p))` iff `U(S(c2, M), S(c2, p))`; +/// - if the first column of `M` is only wildcards, then the function returns `[mc]` on its own. +/// Any function that has those properties ensures correctness of the algorithm. We will of course +/// try to pick a function that also ensures good performance. +/// The idea is that we still need to try different constructors, but we try to keep them grouped +/// together when possible to avoid doing redundant work. /// -/// 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. +/// Here is roughly how splitting works for us: +/// - for wildcards, there are two cases: +/// - if all the possible constructors of the relevant type exist in the first column +/// of `M`, then we return the list of all those constructors, like we did before; +/// - if however some constructors are missing, then it turns out that considering +/// those missing constructors is enough. We return a "missing constructors" meta- +/// contructor that carries the missing constructors in question. +/// (Note the similarity with the algorithm from the paper. It is not a coincidence) +/// - for ranges, we split the range into a disjoint set of subranges, see the code for details; +/// - for slices, we split the slice into a number of fixed-length slices and one longer +/// variable-length slice, again see code; /// -/// - 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))` +/// Thus we get: +/// `U(M, p) := +/// ∃(mc ϵ pat_constructors(p_1)) +/// ∃(mc' ϵ split_metaconstructor(mc, M)) +/// U(S(c, M), S(c, p)) for some c ϵ mc'` +/// TODO: what about empty types ? /// -/// 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. +/// Note that the termination of the algorithm now depends on the behaviour of the splitting +/// phase. However, from the third property of the splitting function, +/// we can see that the depth of splitting of the algorithm is bounded by some +/// function of the depths of the patterns fed to it initially. So we're confident that +/// it terminates. /// -/// Exhaustive integer matching -/// --------------------------- -/// An integer type can be thought of as a (huge) sum type: 1 | 2 | 3 | ... -/// So to support exhaustive integer matching, we can make use of the logic in the paper for -/// OR-patterns. However, we obviously can't just treat ranges x..=y as individual sums, because -/// they are likely gigantic. So we instead treat ranges as constructors of the integers. This means -/// that we have a constructor *of* constructors (the integers themselves). We then need to work -/// through all the inductive step rules above, deriving how the ranges would be treated as -/// OR-patterns, and making sure that they're treated in the same way even when they're ranges. -/// There are really only four special cases here: -/// - When we match on a constructor that's actually a range, we have to treat it as if we would -/// an OR-pattern. -/// + It turns out that we can simply extend the case for single-value patterns in -/// `specialize` to either be *equal* to a value constructor, or *contained within* a range -/// constructor. -/// + When the pattern itself is a range, you just want to tell whether any of the values in -/// the pattern range coincide with values in the constructor range, which is precisely -/// intersection. -/// Since when encountering a range pattern for a value constructor, we also use inclusion, it -/// means that whenever the constructor is a value/range and the pattern is also a value/range, -/// we can simply use intersection to test usefulness. -/// - When we're testing for usefulness of a pattern and the pattern's first component is a -/// wildcard. -/// + If all the constructors appear in the matrix, we have a slight complication. By default, -/// the behaviour (i.e., a disjunction over specialised matrices for each constructor) is -/// 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` 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 -/// can't span wildcards using ranges). +/// This algorithm is equivalent to the one presented in the paper if we only consider +/// wildcards. Thus this mostly extends the original algorithm to ranges and variable-length +/// slices, while removing the special-casing of the wildcard pattern. We also additionally +/// support uninhabited types. use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; From 1bd18d2ee2606f51802c2c09aea607c2bfca3918 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 17:27:21 +0200 Subject: [PATCH 18/87] Define a MissingConstructors struct for cleanliness --- src/librustc_mir/hair/pattern/_match.rs | 141 +++++++++++++++--------- 1 file changed, 91 insertions(+), 50 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 1776c33a7aaf6..7eeb8fe88a716 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1185,43 +1185,87 @@ 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`. +#[derive(Clone)] +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>, +} + +type MissingConstructorsIter<'a, 'tcx, F> = + std::iter::FlatMap>, Vec>, F>; - // 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; +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 } + } + + 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 is_complete(&self) -> bool { + self.used_ctors.is_empty() + } + + /// Iterate over all_ctors \ used_ctors + // Can't use impl Iterator because of lifetime shenanigans + fn iter<'a>( + &'a self, + ) -> MissingConstructorsIter< + 'a, + 'tcx, + impl FnMut(&'a Constructor<'tcx>) -> Vec>, + > { + 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. @@ -1333,6 +1377,14 @@ 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); + + // 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); + // `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. @@ -1353,27 +1405,16 @@ 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. 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); + // non-wildcard patterns in the current column. + let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); debug!( "missing_ctors.is_empty()={:#?} is_privately_empty={:#?}", - missing_ctors.peek().is_none(), + missing_ctors.is_empty(), is_privately_empty, ); - // 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); - - if missing_ctors.peek().is_none() && !is_non_exhaustive { - drop(missing_ctors); // It was borrowing `all_ctors`, which we want to move out of. + if missing_ctors.is_empty() && !is_non_exhaustive { + let (all_ctors, used_ctors) = missing_ctors.into_inner(); split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, &used_ctors, pcx.ty) .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) @@ -1429,7 +1470,7 @@ 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.is_complete() { // 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 }] @@ -1438,7 +1479,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 ad28919c6ac7a1ac49bdd31bfc37bf9dd7a6bdc5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 17:56:56 +0200 Subject: [PATCH 19/87] Move is_non_exhaustive into MissingConstructors --- src/librustc_mir/hair/pattern/_match.rs | 31 +++++++++++++++++++------ 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 7eeb8fe88a716..11fbedb5c8b80 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1192,6 +1192,7 @@ struct MissingConstructors<'tcx> { param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, + is_non_exhaustive: bool, } type MissingConstructorsIter<'a, 'tcx, F> = @@ -1203,14 +1204,18 @@ impl<'tcx> MissingConstructors<'tcx> { param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, + is_non_exhaustive: bool, ) -> Self { - MissingConstructors { tcx, param_env, all_ctors, used_ctors } + MissingConstructors { tcx, param_env, all_ctors, used_ctors, is_non_exhaustive } } fn into_inner(self) -> (Vec>, Vec>) { (self.all_ctors, self.used_ctors) } + fn is_non_exhaustive(&self) -> bool { + self.is_non_exhaustive + } fn is_empty(&self) -> bool { self.iter().next().is_none() } @@ -1264,7 +1269,11 @@ impl<'tcx> MissingConstructors<'tcx> { 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) + f.debug_struct("MissingConstructors") + .field("is_empty", &self.is_empty()) + .field("is_non_exhaustive", &self.is_non_exhaustive) + .field("ctors", &ctors) + .finish() } } @@ -1406,14 +1415,20 @@ pub fn is_useful<'p, 'a, 'tcx>( // Missing constructors are those that are not matched by any // non-wildcard patterns in the current column. - let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, used_ctors); + let missing_ctors = MissingConstructors::new( + cx.tcx, + cx.param_env, + all_ctors, + used_ctors, + is_non_exhaustive, + ); debug!( - "missing_ctors.is_empty()={:#?} is_privately_empty={:#?}", + "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", missing_ctors.is_empty(), - is_privately_empty, + is_non_exhaustive, ); - if missing_ctors.is_empty() && !is_non_exhaustive { + if missing_ctors.is_empty() && !missing_ctors.is_non_exhaustive() { let (all_ctors, used_ctors) = missing_ctors.into_inner(); split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, &used_ctors, pcx.ty) .into_iter() @@ -1470,7 +1485,9 @@ 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 || missing_ctors.is_complete() { + let new_patterns = if missing_ctors.is_non_exhaustive() + || missing_ctors.is_complete() + { // 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 }] From ec57a893b31433b8558bef4daee7b944ac29e513 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 18:16:28 +0200 Subject: [PATCH 20/87] Allow Constructor::apply to return more than one pattern --- src/librustc_mir/hair/pattern/_match.rs | 55 +++++++++++++++---------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 11fbedb5c8b80..798f8b0ae014c 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -645,7 +645,7 @@ impl<'tcx> Constructor<'tcx> { cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>, pats: impl IntoIterator>, - ) -> Pat<'tcx> { + ) -> SmallVec<[Pat<'tcx>; 1]> { let mut pats = pats.into_iter(); let pat = match ty.kind { ty::Adt(..) | ty::Tuple(..) => { @@ -687,11 +687,15 @@ impl<'tcx> Constructor<'tcx> { }, }; - Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } + smallvec!(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> { + fn apply_wildcards<'a>( + &self, + cx: &MatchCheckCtxt<'a, 'tcx>, + ty: Ty<'tcx>, + ) -> SmallVec<[Pat<'tcx>; 1]> { let pats = self.wildcard_subpatterns(cx, ty).rev(); self.apply(cx, ty, pats) } @@ -784,17 +788,22 @@ impl<'tcx> Witness<'tcx> { cx: &MatchCheckCtxt<'a, 'tcx>, ctor: &Constructor<'tcx>, ty: Ty<'tcx>, - ) -> Self { + ) -> SmallVec<[Self; 1]> { let arity = ctor.arity(cx, ty); - let pat = { + let applied_pats = { let len = self.0.len() as u64; let pats = self.0.drain((len - arity) as usize..).rev(); ctor.apply(cx, ty, pats) }; - self.0.push(pat); - - self + applied_pats + .into_iter() + .map(|pat| { + let mut w = self.clone(); + w.0.push(pat); + w + }) + .collect() } } @@ -1485,19 +1494,21 @@ pub fn is_useful<'p, 'a, 'tcx>( // `(, , true)` - we are // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. - let new_patterns = if missing_ctors.is_non_exhaustive() - || missing_ctors.is_complete() - { - // 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() - }; + let new_patterns = + if missing_ctors.is_non_exhaustive() || missing_ctors.is_complete() { + // 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() + .flat_map(|ctor| ctor.apply_wildcards(cx, pcx.ty)) + .collect() + }; // Add the new patterns to each witness let new_witnesses = witnesses .into_iter() @@ -1537,7 +1548,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses .into_iter() - .map(|witness| witness.apply_constructor(cx, &ctor, lty)) + .flat_map(|witness| witness.apply_constructor(cx, &ctor, lty)) .collect(), ), result => result, From 6d9a35d934878d415344f267561356b3466d0784 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 18:35:29 +0200 Subject: [PATCH 21/87] Define MissingConstructors meta-constructor and use it for witness reconstruction --- src/librustc_mir/hair/pattern/_match.rs | 151 +++++++++++++----------- 1 file changed, 82 insertions(+), 69 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 798f8b0ae014c..93599461a3b8b 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -555,8 +555,10 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } +/// Constructors and metaconstructors. #[derive(Clone, Debug, PartialEq)] enum Constructor<'tcx> { + // Base constructors /// The constructor of all patterns that don't vary by constructor, /// e.g., struct patterns and fixed-length arrays. Single, @@ -564,10 +566,17 @@ enum Constructor<'tcx> { Variant(DefId), /// Literal values. ConstantValue(&'tcx ty::Const<'tcx>), - /// Ranges of literal values (`2..=5` and `2..5`). - ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), /// Array patterns of length n. FixedLenSlice(u64), + + // Meta-constructors + /// Ranges of literal values (`2..=5` and `2..5`). + ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), + /// List of constructors that were _not_ present in the first column + /// of the matrix when encountering a wildcard. The contained list must + /// be nonempty. + /// This is only used in the output of metaconstructor splitting. + MissingConstructors(MissingConstructors<'tcx>), } impl<'tcx> Constructor<'tcx> { @@ -683,6 +692,66 @@ impl<'tcx> Constructor<'tcx> { hi: ty::Const::from_bits(cx.tcx, hi, ty::ParamEnv::empty().and(ty)), end, }), + MissingConstructors(ref missing_ctors) => { + // 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 missing_ctors.is_non_exhaustive() || missing_ctors.is_complete() { + // All constructors are unused. Add a wild pattern + // rather than each individual constructor. + 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(_)`. + return missing_ctors + .iter() + .flat_map(|ctor| ctor.apply_wildcards(cx, ty)) + .collect(); + } + } _ => PatKind::Wild, }, }; @@ -1286,6 +1355,15 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { } } +// TODO: we should never need that and it's very expensive +impl<'tcx> PartialEq for MissingConstructors<'tcx> { + fn eq(&self, other: &Self) -> bool { + let self_ctors: Vec<_> = self.iter().collect(); + let other_ctors: Vec<_> = other.iter().collect(); + self_ctors == other_ctors + } +} + /// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. /// The algorithm from the paper has been modified to correctly handle empty /// types. The changes are: @@ -1449,76 +1527,11 @@ pub fn is_useful<'p, 'a, 'tcx>( let v = v.to_tail(); match is_useful(cx, &matrix, &v, witness_preference) { 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 missing_ctors.is_non_exhaustive() || missing_ctors.is_complete() { - // 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() - .flat_map(|ctor| ctor.apply_wildcards(cx, pcx.ty)) - .collect() - }; + let ctor = MissingConstructors(missing_ctors); // 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 - }) - }) + .flat_map(|witness| witness.apply_constructor(cx, &ctor, pcx.ty)) .collect(); UsefulWithWitness(new_witnesses) } From 6e392f04d23d04ba4848e07db9adfa3b0b57bb4b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 18:53:29 +0200 Subject: [PATCH 22/87] Match constructor first instead of type first in various functions Needed since there may now be meta-constructors for the given type. Also implement the trivial MissingConstructors cases for some of those functions. --- src/librustc_mir/hair/pattern/_match.rs | 292 ++++++++++++------------ 1 file changed, 146 insertions(+), 146 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 93599461a3b8b..3dd620f1f8c62 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -593,12 +593,12 @@ impl<'tcx> Constructor<'tcx> { adt: &'tcx ty::AdtDef, ) -> VariantIdx { match self { - &Variant(id) => adt.variant_index_with_id(id), - &Single => { + Variant(id) => adt.variant_index_with_id(*id), + Single => { assert!(!adt.is_enum()); VariantIdx::new(0) } - &ConstantValue(c) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), + ConstantValue(c) => crate::const_eval::const_variant_index(cx.tcx, cx.param_env, c), _ => bug!("bad constructor {:?} for adt {:?}", self, adt), } } @@ -623,18 +623,18 @@ impl<'tcx> Constructor<'tcx> { /// 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 { - FixedLenSlice(length) => length, - ConstantValue(_) => 0, - _ => bug!("bad slice pattern {:?} {:?}", self, ty), + match self { + Single | Variant(_) => match ty.kind { + ty::Tuple(ref fs) => fs.len() as u64, + ty::Ref(..) => 1, + ty::Adt(adt, _) => { + adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64 + } + ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), + _ => 0, }, - ty::Ref(..) => 1, - ty::Adt(adt, _) => { - adt.variants[self.variant_index_for_adt(cx, adt)].fields.len() as u64 - } - _ => 0, + FixedLenSlice(length) => *length, + ConstantValue(_) | ConstantRange(..) | MissingConstructors(_) => 0, } } @@ -656,104 +656,101 @@ impl<'tcx> Constructor<'tcx> { pats: impl IntoIterator>, ) -> SmallVec<[Pat<'tcx>; 1]> { 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, + let pat = match self { + Single | Variant(_) => 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 } } - } else { - PatKind::Leaf { subpatterns: pats } } - } - - ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, - - ty::Slice(_) | ty::Array(..) => { + ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, + _ => PatKind::Wild, + }, + FixedLenSlice(_) => { 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, - }), - MissingConstructors(ref missing_ctors) => { - // 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 missing_ctors.is_non_exhaustive() || missing_ctors.is_complete() { - // All constructors are unused. Add a wild pattern - // rather than each individual constructor. - 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(_)`. - return missing_ctors - .iter() - .flat_map(|ctor| ctor.apply_wildcards(cx, ty)) - .collect(); - } + 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: *end, + }), + MissingConstructors(missing_ctors) => { + // 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 missing_ctors.is_non_exhaustive() || missing_ctors.is_complete() { + // All constructors are unused. Add a wild pattern + // rather than each individual constructor. + 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(_)`. + return missing_ctors + .iter() + .flat_map(|ctor| ctor.apply_wildcards(cx, ty)) + .collect(); } - _ => PatKind::Wild, - }, + } }; smallvec!(Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }) @@ -1628,51 +1625,54 @@ fn constructor_sub_pattern_tys<'a, 'tcx>( ty: Ty<'tcx>, ) -> Vec> { debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty); - match ty.kind { - ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), - ty::Slice(ty) | ty::Array(ty, _) => match *ctor { - FixedLenSlice(length) => (0..length).map(|_| ty).collect(), - ConstantValue(_) => vec![], - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), - }, - ty::Ref(_, rty, _) => vec![rty], - ty::Adt(adt, substs) => { - if adt.is_box() { - // Use T as the sub pattern type of Box. - vec![substs.type_at(0)] - } else { - adt.variants[ctor.variant_index_for_adt(cx, adt)] - .fields - .iter() - .map(|field| { - let is_visible = - adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - if is_visible { - 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 + match ctor { + Single | Variant(_) => match ty.kind { + ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), + ty::Ref(_, rty, _) => vec![rty], + ty::Adt(adt, substs) => { + if adt.is_box() { + // Use T as the sub pattern type of Box. + vec![substs.type_at(0)] + } else { + adt.variants[ctor.variant_index_for_adt(cx, adt)] + .fields + .iter() + .map(|field| { + let is_visible = + adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); + if is_visible { + 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, } - _ => ty, + } else { + // 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. + cx.tcx.types.err } - } else { - // 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. - cx.tcx.types.err - } - }) - .collect() + }) + .collect() + } } - } - _ => vec![], + ty::Slice(ty) | ty::Array(ty, _) => bug!("bad slice pattern {:?} {:?}", ctor, ty), + _ => vec![], + }, + FixedLenSlice(length) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), + _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), + }, + ConstantValue(_) | ConstantRange(..) | MissingConstructors(_) => vec![], } } From d9871b8b05dce7a21e0544264165002c862629b1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 27 Sep 2019 19:01:13 +0200 Subject: [PATCH 23/87] Move constructor_sub_pattern_tys into wildcard_subpatterns --- src/librustc_mir/hair/pattern/_match.rs | 122 +++++++++++------------- 1 file changed, 55 insertions(+), 67 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 3dd620f1f8c62..c53b52ed6af97 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -609,11 +609,60 @@ impl<'tcx> Constructor<'tcx> { cx: &MatchCheckCtxt<'a, 'tcx>, ty: Ty<'tcx>, ) -> impl Iterator> + DoubleEndedIterator { - constructor_sub_pattern_tys(cx, self, ty).into_iter().map(|ty| Pat { - ty, - span: DUMMY_SP, - kind: box PatKind::Wild, - }) + debug!("wildcard_subpatterns({:#?}, {:?})", self, ty); + let subpattern_types = match self { + Single | Variant(_) => match ty.kind { + ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), + ty::Ref(_, rty, _) => vec![rty], + ty::Adt(adt, substs) => { + if adt.is_box() { + // Use T as the sub pattern type of Box. + vec![substs.type_at(0)] + } else { + adt.variants[self.variant_index_for_adt(cx, adt)] + .fields + .iter() + .map(|field| { + let is_visible = adt.is_enum() + || field.vis.is_accessible_from(cx.module, cx.tcx); + if is_visible { + 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, + } + } else { + // 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. + cx.tcx.types.err + } + }) + .collect() + } + } + ty::Slice(ty) | ty::Array(ty, _) => bug!("bad slice pattern {:?} {:?}", self, ty), + _ => vec![], + }, + FixedLenSlice(length) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), + _ => bug!("bad slice pattern {:?} {:?}", self, ty), + }, + ConstantValue(_) | ConstantRange(..) | MissingConstructors(_) => vec![], + }; + + subpattern_types.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 @@ -1615,68 +1664,7 @@ fn pat_constructors<'tcx>( } } -/// This computes the types of the sub patterns that a constructor should be -/// expanded to. -/// -/// For instance, a tuple pattern (43u32, 'a') has sub pattern types [u32, char]. -fn constructor_sub_pattern_tys<'a, 'tcx>( - cx: &MatchCheckCtxt<'a, 'tcx>, - ctor: &Constructor<'tcx>, - ty: Ty<'tcx>, -) -> Vec> { - debug!("constructor_sub_pattern_tys({:#?}, {:?})", ctor, ty); - match ctor { - Single | Variant(_) => match ty.kind { - ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), - ty::Ref(_, rty, _) => vec![rty], - ty::Adt(adt, substs) => { - if adt.is_box() { - // Use T as the sub pattern type of Box. - vec![substs.type_at(0)] - } else { - adt.variants[ctor.variant_index_for_adt(cx, adt)] - .fields - .iter() - .map(|field| { - let is_visible = - adt.is_enum() || field.vis.is_accessible_from(cx.module, cx.tcx); - if is_visible { - 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, - } - } else { - // 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. - cx.tcx.types.err - } - }) - .collect() - } - } - ty::Slice(ty) | ty::Array(ty, _) => bug!("bad slice pattern {:?} {:?}", ctor, ty), - _ => vec![], - }, - FixedLenSlice(length) => match ty.kind { - ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), - _ => bug!("bad slice pattern {:?} {:?}", ctor, ty), - }, - ConstantValue(_) | ConstantRange(..) | MissingConstructors(_) => vec![], - } -} - -// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, +// Checks whether a constant is equal to a user-written slice pattern. Only supports byte slices, // meaning all other types will compare unequal and thus equal patterns often do not cause the // second pattern to lint about unreachable match arms. fn slice_pat_covered_by_const<'tcx>( From cf90304aac3e2b5c398b211b7f7f1901d3447862 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 02:03:27 +0200 Subject: [PATCH 24/87] Replace wildcard-specific code paths with generalized specialization --- src/librustc_mir/hair/pattern/_match.rs | 70 +++++++++---------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index c53b52ed6af97..c1146127e5d32 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -302,15 +302,6 @@ 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, - } - } -} - /// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` /// works well. #[derive(Debug, Clone)] @@ -320,6 +311,9 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { pub fn from_pattern(pat: &'p Pat<'tcx>) -> Self { PatStack(smallvec![pat]) } + fn empty() -> Self { + PatStack(smallvec![]) + } fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { PatStack(vec) } @@ -339,17 +333,10 @@ impl<'p, 'tcx> PatStack<'p, '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) } - /// This computes `D(self)`. See top of the file for explanations. - fn pop_wildcard(&self) -> Option { - if self.0[0].is_wildcard() { Some(self.to_tail()) } else { None } - } /// This computes `S(constructor, self)`. See top of the file for explanations. fn pop_constructor<'a, 'p2>( &self, @@ -362,10 +349,12 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { 'p: 'p2, { let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); - new_heads.map(|mut new_head| { + let result = new_heads.map(|mut new_head| { new_head.0.extend_from_slice(&self.0[1..]); new_head - }) + }); + debug!("specialize({:#?}, {:#?}) = {:#?}", self, constructor, result); + result } } @@ -411,10 +400,6 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.0.iter().map(|r| r.head()) } - /// This computes `D(self)`. See top of the file for explanations. - fn pop_wildcard(&self) -> Self { - self.0.iter().filter_map(|r| r.pop_wildcard()).collect() - } /// This computes `S(constructor, self)`. See top of the file for explanations. fn pop_constructor<'a, 'p2>( &self, @@ -1569,20 +1554,8 @@ pub fn is_useful<'p, 'a, 'tcx>( .find(|result| result.is_useful()) .unwrap_or(NotUseful) } else { - let matrix = matrix.pop_wildcard(); - let v = v.to_tail(); - match is_useful(cx, &matrix, &v, witness_preference) { - UsefulWithWitness(witnesses) => { - let ctor = MissingConstructors(missing_ctors); - // Add the new patterns to each witness - let new_witnesses = witnesses - .into_iter() - .flat_map(|witness| witness.apply_constructor(cx, &ctor, pcx.ty)) - .collect(); - UsefulWithWitness(new_witnesses) - } - result => result, - } + let ctor = MissingConstructors(missing_ctors); + is_useful_specialized(cx, &matrix, &v, ctor, pcx.ty, witness_preference) } } } @@ -1936,13 +1909,25 @@ fn patterns_for_variant<'p, 'tcx>( /// fields filled with wild patterns. fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, - pat: &'p2 Pat<'tcx>, + mut pat: &'p2 Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> Option> { - let result = match *pat.kind { - PatKind::AscribeUserType { ref subpattern, .. } => PatStack::from_pattern(subpattern) - .pop_constructor(cx, constructor, ctor_wild_subpatterns), + while let PatKind::AscribeUserType { ref subpattern, .. } = *pat.kind { + pat = subpattern; + } + + if let MissingConstructors(_) = constructor { + // By construction of MissingConstructors, we know that all non-wildcard constructors + // should be discarded. + return match *pat.kind { + PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), + _ => None, + }; + } + + match *pat.kind { + PatKind::AscribeUserType { .. } => unreachable!(), // Handled above PatKind::Binding { .. } | PatKind::Wild => { Some(PatStack::from_slice(ctor_wild_subpatterns)) @@ -2093,8 +2078,5 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( PatKind::Or { .. } => { bug!("support for or-patterns has not been fully implemented yet."); } - }; - debug!("specialize({:#?}, {:#?}) = {:#?}", pat, ctor_wild_subpatterns, result); - - result + } } From 931d7a9cc4321c8192ed026e86b7f3f747ffcd3a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 02:20:25 +0200 Subject: [PATCH 25/87] Factor out some bits --- src/librustc_mir/hair/pattern/_match.rs | 66 ++++++++++++++----------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index c1146127e5d32..e227b4714b921 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -809,12 +809,36 @@ 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>, + lty: Ty<'tcx>, + ) -> Self { + match self { + UsefulWithWitness(witnesses) => UsefulWithWitness( + witnesses + .into_iter() + .flat_map(|witness| witness.apply_constructor(cx, &ctor, lty)) + .collect(), + ), + x => x, + } + } } #[derive(Copy, Clone, Debug)] @@ -1433,10 +1457,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 }; @@ -1489,13 +1510,9 @@ pub fn is_useful<'p, 'a, 'tcx>( .collect(); debug!("used_ctors = {:#?}", used_ctors); - if let Some(constructors) = v_constructors { + let constructors = if let Some(constructors) = v_constructors { debug!("is_useful - expanding constructors: {:#?}", constructors); split_grouped_constructors(cx.tcx, cx.param_env, constructors, &used_ctors, pcx.ty) - .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) } else { debug!("is_useful - expanding wildcard"); @@ -1549,15 +1566,16 @@ pub fn is_useful<'p, 'a, 'tcx>( if missing_ctors.is_empty() && !missing_ctors.is_non_exhaustive() { let (all_ctors, used_ctors) = missing_ctors.into_inner(); split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, &used_ctors, pcx.ty) - .into_iter() - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) } else { - let ctor = MissingConstructors(missing_ctors); - is_useful_specialized(cx, &matrix, &v, ctor, pcx.ty, witness_preference) + vec![MissingConstructors(missing_ctors)] } - } + }; + + constructors + .into_iter() + .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful) } /// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied @@ -1575,18 +1593,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.pop_constructor(cx, &ctor, &ctor_wild_subpatterns); - match v.pop_constructor(cx, &ctor, &ctor_wild_subpatterns) { - Some(v) => match is_useful(cx, &matrix, &v, witness_preference) { - UsefulWithWitness(witnesses) => UsefulWithWitness( - witnesses - .into_iter() - .flat_map(|witness| witness.apply_constructor(cx, &ctor, lty)) - .collect(), - ), - result => result, - }, - None => NotUseful, - } + v.pop_constructor(cx, &ctor, &ctor_wild_subpatterns) + .map(|v| is_useful(cx, &matrix, &v, witness_preference)) + .map(|u| u.apply_constructor(cx, &ctor, lty)) + .unwrap_or(NotUseful) } /// Determines the constructors that the given pattern can be specialized to. From 142df8be8ba711730ea9bdfd2a3392f414f12c99 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 02:36:29 +0200 Subject: [PATCH 26/87] Extract constructor splitting as a Constructor method --- src/librustc_mir/hair/pattern/_match.rs | 227 ++++++++++++------------ 1 file changed, 114 insertions(+), 113 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index e227b4714b921..d02b2451504b8 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -588,6 +588,110 @@ impl<'tcx> Constructor<'tcx> { } } + /// Split a metaconstructor into equivalence classes of constructors that behave the same + /// for the given matrix. See description of the algorithm for details. + fn split_meta_constructor( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + head_ctors: &Vec>, + ty: Ty<'tcx>, + ) -> SmallVec<[Constructor<'tcx>; 1]> { + match &self { + ConstantRange(..) if should_treat_range_exhaustively(tcx, &self) => { + // For exhaustive integer matching, some constructors are grouped within other constructors + // (namely integer typed values are grouped within ranges). However, when specialising these + // constructors, we want to be specialising for the underlying constructors (the integers), not + // the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would + // mean creating a separate constructor for every single value in the range, which is clearly + // impractical. However, observe that for some ranges of integers, the specialisation will be + // identical across all values in that range (i.e., there are equivalence classes of ranges of + // constructors based on their `is_useful_specialized` outcome). These classes are grouped by + // the patterns that apply to them (in the matrix `P`). We can split the range whenever the + // patterns that apply to that range (specifically: the patterns that *intersect* with that range) + // change. + // Our solution, therefore, is to split the range constructor into subranges at every single point + // the group of intersecting patterns changes (using the method described below). + // And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching + // on actual integers. The nice thing about this is that the number of subranges is linear in the + // number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't + // need to be worried about matching over gargantuan ranges. + // + // Essentially, given the first column of a matrix representing ranges, looking like the following: + // + // |------| |----------| |-------| || + // |-------| |-------| |----| || + // |---------| + // + // We split the ranges up into equivalence classes so the ranges are no longer overlapping: + // + // |--|--|||-||||--||---|||-------| |-|||| || + // + // The logic for determining how to split the ranges is fairly straightforward: we calculate + // boundaries for each interval range, sort them, then create constructors for each new interval + // between every pair of boundary points. (This essentially sums up to performing the intuitive + // merging operation depicted above.) + + // We only care about finding all the subranges within the range of the constructor + // range. Anything else is irrelevant, because it is guaranteed to result in + // `NotUseful`, which is the default case anyway, and can be ignored. + let ctor_range = IntRange::from_ctor(tcx, param_env, &self).unwrap(); + + /// Represents a border between 2 integers. Because the intervals spanning borders + /// must be able to cover every integer, we need to be able to represent + /// 2^128 + 1 such borders. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + enum Border { + JustBefore(u128), + AfterMax, + } + + // A function for extracting the borders of an integer interval. + fn range_borders(r: IntRange<'_>) -> impl Iterator { + let (lo, hi) = r.range.into_inner(); + let from = Border::JustBefore(lo); + let to = match hi.checked_add(1) { + Some(m) => Border::JustBefore(m), + None => Border::AfterMax, + }; + vec![from, to].into_iter() + } + + // `borders` is the set of borders between equivalence classes: each equivalence + // class lies between 2 borders. + let row_borders = head_ctors + .iter() + .flat_map(|ctor| IntRange::from_ctor(tcx, param_env, ctor)) + .flat_map(|range| ctor_range.intersection(&range)) + .flat_map(|range| range_borders(range)); + let ctor_borders = range_borders(ctor_range.clone()); + let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); + borders.sort_unstable(); + + // 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. + borders + .windows(2) + .filter_map(|window| match (window[0], window[1]) { + (Border::JustBefore(n), Border::JustBefore(m)) => { + if n < m { + Some(n..=(m - 1)) + } else { + None + } + } + (Border::JustBefore(n), Border::AfterMax) => Some(n..=u128::MAX), + (Border::AfterMax, _) => None, + }) + .map(|range| IntRange::range_to_ctor(tcx, ty, range)) + .collect() + } + // Any other constructor can be used unchanged. + _ => smallvec![self], + } + } + /// This returns one wildcard pattern for each argument to this constructor. fn wildcard_subpatterns<'a>( &self, @@ -1512,7 +1616,10 @@ pub fn is_useful<'p, 'a, 'tcx>( let constructors = if let Some(constructors) = v_constructors { debug!("is_useful - expanding constructors: {:#?}", constructors); - split_grouped_constructors(cx.tcx, cx.param_env, constructors, &used_ctors, pcx.ty) + constructors + .into_iter() + .flat_map(|ctor| ctor.split_meta_constructor(cx.tcx, cx.param_env, &used_ctors, pcx.ty)) + .collect() } else { debug!("is_useful - expanding wildcard"); @@ -1565,7 +1672,12 @@ pub fn is_useful<'p, 'a, 'tcx>( if missing_ctors.is_empty() && !missing_ctors.is_non_exhaustive() { let (all_ctors, used_ctors) = missing_ctors.into_inner(); - split_grouped_constructors(cx.tcx, cx.param_env, all_ctors, &used_ctors, pcx.ty) + all_ctors + .into_iter() + .flat_map(|ctor| { + ctor.split_meta_constructor(cx.tcx, cx.param_env, &used_ctors, pcx.ty) + }) + .collect() } else { vec![MissingConstructors(missing_ctors)] } @@ -1722,117 +1834,6 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) } } -/// For exhaustive integer matching, some constructors are grouped within other constructors -/// (namely integer typed values are grouped within ranges). However, when specialising these -/// constructors, we want to be specialising for the underlying constructors (the integers), not -/// the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would -/// mean creating a separate constructor for every single value in the range, which is clearly -/// impractical. However, observe that for some ranges of integers, the specialisation will be -/// identical across all values in that range (i.e., there are equivalence classes of ranges of -/// constructors based on their `is_useful_specialized` outcome). These classes are grouped by -/// the patterns that apply to them (in the matrix `P`). We can split the range whenever the -/// patterns that apply to that range (specifically: the patterns that *intersect* with that range) -/// change. -/// Our solution, therefore, is to split the range constructor into subranges at every single point -/// the group of intersecting patterns changes (using the method described below). -/// And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching -/// on actual integers. The nice thing about this is that the number of subranges is linear in the -/// number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't -/// need to be worried about matching over gargantuan ranges. -/// -/// Essentially, given the first column of a matrix representing ranges, looking like the following: -/// -/// |------| |----------| |-------| || -/// |-------| |-------| |----| || -/// |---------| -/// -/// We split the ranges up into equivalence classes so the ranges are no longer overlapping: -/// -/// |--|--|||-||||--||---|||-------| |-|||| || -/// -/// The logic for determining how to split the ranges is fairly straightforward: we calculate -/// boundaries for each interval range, sort them, then create constructors for each new interval -/// between every pair of boundary points. (This essentially sums up to performing the intuitive -/// merging operation depicted above.) -fn split_grouped_constructors<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctors: Vec>, - matrix_ctors: &Vec>, - ty: Ty<'tcx>, -) -> Vec> { - let mut split_ctors = Vec::with_capacity(ctors.len()); - - for ctor in ctors { - match ctor { - // For now, only ranges may denote groups of "subconstructors", so we only need to - // special-case constant ranges. - ConstantRange(..) if should_treat_range_exhaustively(tcx, &ctor) => { - // We only care about finding all the subranges within the range of the constructor - // range. Anything else is irrelevant, because it is guaranteed to result in - // `NotUseful`, which is the default case anyway, and can be ignored. - let ctor_range = IntRange::from_ctor(tcx, param_env, &ctor).unwrap(); - - /// Represents a border between 2 integers. Because the intervals spanning borders - /// must be able to cover every integer, we need to be able to represent - /// 2^128 + 1 such borders. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - enum Border { - JustBefore(u128), - AfterMax, - } - - // A function for extracting the borders of an integer interval. - fn range_borders(r: IntRange<'_>) -> impl Iterator { - let (lo, hi) = r.range.into_inner(); - let from = Border::JustBefore(lo); - let to = match hi.checked_add(1) { - Some(m) => Border::JustBefore(m), - None => Border::AfterMax, - }; - vec![from, to].into_iter() - } - - // `borders` is the set of borders between equivalence classes: each equivalence - // class lies between 2 borders. - let row_borders = matrix_ctors - .iter() - .flat_map(|ctor| IntRange::from_ctor(tcx, param_env, ctor)) - .flat_map(|range| ctor_range.intersection(&range)) - .flat_map(|range| range_borders(range)); - let ctor_borders = range_borders(ctor_range.clone()); - let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); - borders.sort_unstable(); - - // 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)) => { - if n < m { - Some(IntRange { range: n..=(m - 1), ty }) - } else { - None - } - } - (Border::JustBefore(n), Border::AfterMax) => { - Some(IntRange { range: n..=u128::MAX, ty }) - } - (Border::AfterMax, _) => None, - }) - { - split_ctors.push(IntRange::range_to_ctor(tcx, ty, range)); - } - } - // Any other constructor can be used unchanged. - _ => split_ctors.push(ctor), - } - } - - split_ctors -} - fn constructor_covered_by_range<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, From cb4715df1e5b1bf8bdb04d5d18014392d4c61fc6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 02:37:32 +0200 Subject: [PATCH 27/87] Rename pop_constructor back to specialize --- src/librustc_mir/hair/pattern/_match.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index d02b2451504b8..56b1feff3b1d4 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -338,7 +338,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } /// This computes `S(constructor, self)`. See top of the file for explanations. - fn pop_constructor<'a, 'p2>( + fn specialize<'a, 'p2>( &self, cx: &mut MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, @@ -401,7 +401,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// This computes `S(constructor, self)`. See top of the file for explanations. - fn pop_constructor<'a, 'p2>( + fn specialize<'a, 'p2>( &self, cx: &mut MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, @@ -414,7 +414,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { Matrix( self.0 .iter() - .flat_map(|r| r.pop_constructor(cx, constructor, ctor_wild_subpatterns)) + .flat_map(|r| r.specialize(cx, constructor, ctor_wild_subpatterns)) .collect(), ) } @@ -1704,8 +1704,8 @@ 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.pop_constructor(cx, &ctor, &ctor_wild_subpatterns); - v.pop_constructor(cx, &ctor, &ctor_wild_subpatterns) + let matrix = matrix.specialize(cx, &ctor, &ctor_wild_subpatterns); + v.specialize(cx, &ctor, &ctor_wild_subpatterns) .map(|v| is_useful(cx, &matrix, &v, witness_preference)) .map(|u| u.apply_constructor(cx, &ctor, lty)) .unwrap_or(NotUseful) From a1b380005ff94a1a8a7da1acecb46a7f83a69e20 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 12:34:37 +0200 Subject: [PATCH 28/87] Add Wildcard meta-constructor --- src/librustc_mir/hair/pattern/_match.rs | 174 +++++++++++++----------- 1 file changed, 93 insertions(+), 81 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 56b1feff3b1d4..c745957228980 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -557,6 +557,8 @@ enum Constructor<'tcx> { // Meta-constructors /// Ranges of literal values (`2..=5` and `2..5`). ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), + /// Wildcard metaconstructor. + Wildcard, /// List of constructors that were _not_ present in the first column /// of the matrix when encountering a wildcard. The contained list must /// be nonempty. @@ -592,13 +594,15 @@ impl<'tcx> Constructor<'tcx> { /// for the given matrix. See description of the algorithm for details. fn split_meta_constructor( self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + cx: &MatchCheckCtxt<'_, 'tcx>, + pcx: PatCtxt<'tcx>, head_ctors: &Vec>, - ty: Ty<'tcx>, ) -> SmallVec<[Constructor<'tcx>; 1]> { + debug!("split_meta_constructor {:?}", self); match &self { - ConstantRange(..) if should_treat_range_exhaustively(tcx, &self) => { + // Any base constructor can be used unchanged. + Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], + ConstantRange(..) if should_treat_range_exhaustively(cx.tcx, &self) => { // For exhaustive integer matching, some constructors are grouped within other constructors // (namely integer typed values are grouped within ranges). However, when specialising these // constructors, we want to be specialising for the underlying constructors (the integers), not @@ -635,7 +639,7 @@ impl<'tcx> Constructor<'tcx> { // We only care about finding all the subranges within the range of the constructor // range. Anything else is irrelevant, because it is guaranteed to result in // `NotUseful`, which is the default case anyway, and can be ignored. - let ctor_range = IntRange::from_ctor(tcx, param_env, &self).unwrap(); + let ctor_range = IntRange::from_ctor(cx.tcx, cx.param_env, &self).unwrap(); /// Represents a border between 2 integers. Because the intervals spanning borders /// must be able to cover every integer, we need to be able to represent @@ -661,7 +665,7 @@ impl<'tcx> Constructor<'tcx> { // class lies between 2 borders. let row_borders = head_ctors .iter() - .flat_map(|ctor| IntRange::from_ctor(tcx, param_env, ctor)) + .flat_map(|ctor| IntRange::from_ctor(cx.tcx, cx.param_env, ctor)) .flat_map(|range| ctor_range.intersection(&range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); @@ -684,11 +688,74 @@ impl<'tcx> Constructor<'tcx> { (Border::JustBefore(n), Border::AfterMax) => Some(n..=u128::MAX), (Border::AfterMax, _) => None, }) - .map(|range| IntRange::range_to_ctor(tcx, ty, range)) + .map(|range| IntRange::range_to_ctor(cx.tcx, pcx.ty, range)) .collect() } - // Any other constructor can be used unchanged. - _ => smallvec![self], + ConstantRange(..) => smallvec![self], + Wildcard => { + let is_declared_nonexhaustive = + !cx.is_local(pcx.ty) && cx.is_non_exhaustive_enum(pcx.ty); + + // `all_ctors` are all the constructors for the given type, which + // should all be represented (or caught with the wild pattern `_`). + let all_ctors = all_constructors(cx, pcx); + + let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + + // 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); + + // `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. + // + // Therefore, if there is some pattern that is unmatched by `matrix`, + // it will still be unmatched if the first constructor is replaced by + // any of the constructors in `missing_ctors` + // + // However, if our scrutinee is *privately* an empty enum, we + // must treat it as though it had an "unknown" constructor (in + // that case, all other patterns obviously can't be variants) + // to avoid exposing its emptyness. See the `match_privately_empty` + // test for details. + // + // FIXME: currently the only way I know of something can + // be a privately-empty enum is when the exhaustive_patterns + // feature flag is not present, so this is only + // needed for that case. + + // Missing constructors are those that are not matched by any + // non-wildcard patterns in the current column. + let missing_ctors = MissingConstructors::new( + cx.tcx, + cx.param_env, + all_ctors, + head_ctors.clone(), + is_non_exhaustive, + ); + debug!( + "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", + missing_ctors.is_empty(), + is_non_exhaustive, + ); + + if missing_ctors.is_empty() && !is_non_exhaustive { + let (all_ctors, _) = missing_ctors.into_inner(); + // Recursively split newly generated list of constructors. This list must not contain + // any wildcards so we don't recurse infinitely. + all_ctors + .into_iter() + .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, head_ctors)) + .collect() + } else { + smallvec![MissingConstructors(missing_ctors)] + } + } + MissingConstructors(_) => bug!("shouldn't try to split constructor {:?}", self), } } @@ -748,7 +815,7 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, - ConstantValue(_) | ConstantRange(..) | MissingConstructors(_) => vec![], + ConstantValue(_) | MissingConstructors(_) | ConstantRange(..) | Wildcard => vec![], }; subpattern_types.into_iter().map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }) @@ -772,7 +839,7 @@ impl<'tcx> Constructor<'tcx> { _ => 0, }, FixedLenSlice(length) => *length, - ConstantValue(_) | ConstantRange(..) | MissingConstructors(_) => 0, + ConstantValue(_) | ConstantRange(..) | Wildcard | MissingConstructors(_) => 0, } } @@ -829,6 +896,7 @@ impl<'tcx> Constructor<'tcx> { hi: ty::Const::from_bits(cx.tcx, *hi, ty::ParamEnv::empty().and(ty)), end: *end, }), + Wildcard => PatKind::Wild, MissingConstructors(missing_ctors) => { // In this case, there's at least one "free" // constructor that is only matched against by @@ -1043,7 +1111,7 @@ impl<'tcx> Witness<'tcx> { /// We make sure to omit constructors that are statically impossible. E.g., for /// `Option`, we do not include `Some(_)` in the returned list of constructors. fn all_constructors<'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, pcx: PatCtxt<'tcx>, ) -> Vec> { debug!("all_constructors({:?})", pcx.ty); @@ -1597,15 +1665,13 @@ pub fn is_useful<'p, 'a, 'tcx>( let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head(), pcx); - let is_declared_nonexhaustive = !cx.is_local(pcx.ty) - && if v_constructors.is_some() { - cx.is_non_exhaustive_variant(v.head()) - } else { - cx.is_non_exhaustive_enum(pcx.ty) - }; - debug!("is_useful - is_declared_nonexhaustive: {:?}", is_declared_nonexhaustive); - if v_constructors.is_some() && is_declared_nonexhaustive { - return Useful; + if v_constructors.is_some() { + let is_declared_nonexhaustive = + !cx.is_local(pcx.ty) && cx.is_non_exhaustive_variant(v.head()); + debug!("is_useful - is_declared_nonexhaustive: {:?}", is_declared_nonexhaustive); + if is_declared_nonexhaustive { + return Useful; + } } let used_ctors: Vec> = matrix @@ -1618,69 +1684,11 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful - expanding constructors: {:#?}", constructors); constructors .into_iter() - .flat_map(|ctor| ctor.split_meta_constructor(cx.tcx, cx.param_env, &used_ctors, pcx.ty)) + .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &used_ctors)) .collect() } else { debug!("is_useful - expanding wildcard"); - - // `all_ctors` are all the constructors for the given type, which - // should all be represented (or caught with the wild pattern `_`). - 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); - - // 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); - - // `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. - // - // Therefore, if there is some pattern that is unmatched by `matrix`, - // it will still be unmatched if the first constructor is replaced by - // any of the constructors in `missing_ctors` - // - // However, if our scrutinee is *privately* an empty enum, we - // must treat it as though it had an "unknown" constructor (in - // that case, all other patterns obviously can't be variants) - // to avoid exposing its emptyness. See the `match_privately_empty` - // test for details. - // - // FIXME: currently the only way I know of something can - // be a privately-empty enum is when the exhaustive_patterns - // feature flag is not present, so this is only - // needed for that case. - - // Missing constructors are those that are not matched by any - // non-wildcard patterns in the current column. - let missing_ctors = MissingConstructors::new( - cx.tcx, - cx.param_env, - all_ctors, - used_ctors, - is_non_exhaustive, - ); - debug!( - "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", - missing_ctors.is_empty(), - is_non_exhaustive, - ); - - if missing_ctors.is_empty() && !missing_ctors.is_non_exhaustive() { - let (all_ctors, used_ctors) = missing_ctors.into_inner(); - all_ctors - .into_iter() - .flat_map(|ctor| { - ctor.split_meta_constructor(cx.tcx, cx.param_env, &used_ctors, pcx.ty) - }) - .collect() - } else { - vec![MissingConstructors(missing_ctors)] - } + Wildcard.split_meta_constructor(cx, pcx, &used_ctors) }; constructors @@ -1935,6 +1943,10 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), _ => None, }; + } else if let Wildcard = constructor { + bug!( + "tried to specialize on the Wildcard meta-constructor; maybe you wanted to use MissingConstructors" + ) } match *pat.kind { From 6337864ce856677516af69b6f43f508afdefb70f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 12:52:32 +0200 Subject: [PATCH 29/87] pat_constructors now returns Wildcard when relevant --- src/librustc_mir/hair/pattern/_match.rs | 61 +++++++++++++------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index c745957228980..872efb05d0e60 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -574,6 +574,13 @@ impl<'tcx> Constructor<'tcx> { } } + fn is_wildcard(&self) -> bool { + match self { + Wildcard => true, + _ => false, + } + } + fn variant_index_for_adt<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, @@ -1665,31 +1672,25 @@ pub fn is_useful<'p, 'a, 'tcx>( let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head(), pcx); - if v_constructors.is_some() { - let is_declared_nonexhaustive = - !cx.is_local(pcx.ty) && cx.is_non_exhaustive_variant(v.head()); - debug!("is_useful - is_declared_nonexhaustive: {:?}", is_declared_nonexhaustive); - if is_declared_nonexhaustive { - return Useful; - } + if cx.is_non_exhaustive_variant(v.head()) + && !cx.is_local(pcx.ty) + && !v_constructors.iter().any(|ctor| ctor.is_wildcard()) + { + debug!("is_useful - shortcut because declared non-exhaustive"); + return Useful; } - let used_ctors: Vec> = matrix + let matrix_head_ctors: Vec> = matrix .heads() - .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, pcx).unwrap_or(vec![])) + .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, pcx)) + .filter(|ctor| !ctor.is_wildcard()) .collect(); - debug!("used_ctors = {:#?}", used_ctors); + debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); - let constructors = if let Some(constructors) = v_constructors { - debug!("is_useful - expanding constructors: {:#?}", constructors); - constructors - .into_iter() - .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &used_ctors)) - .collect() - } else { - debug!("is_useful - expanding wildcard"); - Wildcard.split_meta_constructor(cx, pcx, &used_ctors) - }; + let constructors: Vec<_> = v_constructors + .into_iter() + .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &matrix_head_ctors)) + .collect(); constructors .into_iter() @@ -1732,33 +1733,33 @@ fn pat_constructors<'tcx>( param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, pcx: PatCtxt<'tcx>, -) -> Option>> { +) -> Vec> { match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { pat_constructors(tcx, param_env, subpattern, pcx) } - PatKind::Binding { .. } | PatKind::Wild => None, - PatKind::Leaf { .. } | PatKind::Deref { .. } => Some(vec![Single]), + PatKind::Binding { .. } | PatKind::Wild => vec![Wildcard], + PatKind::Leaf { .. } | PatKind::Deref { .. } => vec![Single], PatKind::Variant { adt_def, variant_index, .. } => { - Some(vec![Variant(adt_def.variants[variant_index].def_id)]) + vec![Variant(adt_def.variants[variant_index].def_id)] } - PatKind::Constant { value } => Some(vec![ConstantValue(value)]), - PatKind::Range(PatRange { lo, hi, end }) => Some(vec![ConstantRange( + PatKind::Constant { value } => vec![ConstantValue(value)], + PatKind::Range(PatRange { lo, hi, end }) => vec![ConstantRange( lo.eval_bits(tcx, param_env, lo.ty), hi.eval_bits(tcx, param_env, hi.ty), lo.ty, end, - )]), + )], PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => Some(vec![FixedLenSlice(length.eval_usize(tcx, param_env))]), + ty::Array(_, length) => vec![FixedLenSlice(length.eval_usize(tcx, 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(FixedLenSlice).collect()) + (pat_len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() } else { - Some(vec![FixedLenSlice(pat_len)]) + vec![FixedLenSlice(pat_len)] } } PatKind::Or { .. } => { From c0359ce5807f4fd840ca1bf4bb55074fd42b0df6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 12:53:58 +0200 Subject: [PATCH 30/87] Avoid allocating in the common case of a single pattern --- src/librustc_mir/hair/pattern/_match.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 872efb05d0e60..f153d33943371 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1733,25 +1733,25 @@ fn pat_constructors<'tcx>( param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, pcx: PatCtxt<'tcx>, -) -> Vec> { +) -> SmallVec<[Constructor<'tcx>; 1]> { match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { pat_constructors(tcx, param_env, subpattern, pcx) } - PatKind::Binding { .. } | PatKind::Wild => vec![Wildcard], - PatKind::Leaf { .. } | PatKind::Deref { .. } => vec![Single], + PatKind::Binding { .. } | PatKind::Wild => smallvec![Wildcard], + PatKind::Leaf { .. } | PatKind::Deref { .. } => smallvec![Single], PatKind::Variant { adt_def, variant_index, .. } => { - vec![Variant(adt_def.variants[variant_index].def_id)] + smallvec![Variant(adt_def.variants[variant_index].def_id)] } - PatKind::Constant { value } => vec![ConstantValue(value)], - PatKind::Range(PatRange { lo, hi, end }) => vec![ConstantRange( + PatKind::Constant { value } => smallvec![ConstantValue(value)], + PatKind::Range(PatRange { lo, hi, end }) => smallvec![ConstantRange( lo.eval_bits(tcx, param_env, lo.ty), hi.eval_bits(tcx, param_env, hi.ty), lo.ty, end, )], PatKind::Array { .. } => match pcx.ty.kind { - ty::Array(_, length) => vec![FixedLenSlice(length.eval_usize(tcx, param_env))], + ty::Array(_, length) => smallvec![FixedLenSlice(length.eval_usize(tcx, param_env))], _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { @@ -1759,7 +1759,7 @@ fn pat_constructors<'tcx>( if slice.is_some() { (pat_len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() } else { - vec![FixedLenSlice(pat_len)] + smallvec![FixedLenSlice(pat_len)] } } PatKind::Or { .. } => { From 33273f231cde35ef8bcf95953207f15847c20c68 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 12:57:44 +0200 Subject: [PATCH 31/87] Invert condition for clarity --- 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 f153d33943371..2f93676206de4 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -561,7 +561,7 @@ enum Constructor<'tcx> { Wildcard, /// List of constructors that were _not_ present in the first column /// of the matrix when encountering a wildcard. The contained list must - /// be nonempty. + /// be nonempty unless the type is non-exhaustive. /// This is only used in the output of metaconstructor splitting. MissingConstructors(MissingConstructors<'tcx>), } @@ -750,7 +750,9 @@ impl<'tcx> Constructor<'tcx> { is_non_exhaustive, ); - if missing_ctors.is_empty() && !is_non_exhaustive { + if !missing_ctors.is_empty() || is_non_exhaustive { + smallvec![MissingConstructors(missing_ctors)] + } else { let (all_ctors, _) = missing_ctors.into_inner(); // Recursively split newly generated list of constructors. This list must not contain // any wildcards so we don't recurse infinitely. @@ -758,8 +760,6 @@ impl<'tcx> Constructor<'tcx> { .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, head_ctors)) .collect() - } else { - smallvec![MissingConstructors(missing_ctors)] } } MissingConstructors(_) => bug!("shouldn't try to split constructor {:?}", self), From cb3452c69e76058f4b8ebbe0a2aa2e5ef92f72a2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 13:13:34 +0200 Subject: [PATCH 32/87] Clarify handling of uninhabited types --- src/librustc_mir/hair/pattern/_match.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 2f93676206de4..495edc8c85186 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -156,7 +156,9 @@ /// - for each metaconstructor `k` in the output, all the `c ϵ k` behave the same relative /// to `M`. More precisely, we want that for any two `c1` and `c2` in `k`, /// `U(S(c1, M), S(c1, p))` iff `U(S(c2, M), S(c2, p))`; -/// - if the first column of `M` is only wildcards, then the function returns `[mc]` on its own. +/// - if the first column of `M` is only wildcards, then the function returns at most +/// `[mc]` on its own; +/// - if the relevant type is uninhabited, the function returns nothing. /// Any function that has those properties ensures correctness of the algorithm. We will of course /// try to pick a function that also ensures good performance. /// The idea is that we still need to try different constructors, but we try to keep them grouped @@ -174,12 +176,12 @@ /// - for slices, we split the slice into a number of fixed-length slices and one longer /// variable-length slice, again see code; /// -/// Thus we get: -/// `U(M, p) := -/// ∃(mc ϵ pat_constructors(p_1)) -/// ∃(mc' ϵ split_metaconstructor(mc, M)) -/// U(S(c, M), S(c, p)) for some c ϵ mc'` -/// TODO: what about empty types ? +/// Thus we get the new inductive step (i.e. when `n > 0`): +/// `U(M, p) := +/// ∃(mc ϵ pat_constructors(p_1)) +/// ∃(mc' ϵ split_metaconstructor(mc, M)) +/// U(S(c, M), S(c, p)) for some c ϵ mc'` +/// Note: in the case of an uninhabited type, there won't be any `mc'` so this just returns false. /// /// Note that the termination of the algorithm now depends on the behaviour of the splitting /// phase. However, from the third property of the splitting function, @@ -756,6 +758,8 @@ impl<'tcx> Constructor<'tcx> { let (all_ctors, _) = missing_ctors.into_inner(); // Recursively split newly generated list of constructors. This list must not contain // any wildcards so we don't recurse infinitely. + // Note: If the type is uninhabited and we're not in the privately_empty case, then this will + // return an empty list. all_ctors .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, head_ctors)) From ad78eff7ad97418bff5cb6cc5383936ef36b4913 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 28 Sep 2019 16:05:38 +0200 Subject: [PATCH 33/87] Gather together usefulness tests I took most tests that were testing only for match exhaustiveness, pattern refutability or match arm reachability, and put them in the same test folder. --- src/librustc_mir/hair/pattern/_match.rs | 3 +++ .../usefulness}/always-inhabited-union-ref.rs | 0 .../usefulness}/always-inhabited-union-ref.stderr | 0 .../{ => pattern/usefulness}/exhaustive_integer_patterns.rs | 0 .../usefulness}/exhaustive_integer_patterns.stderr | 0 .../ui/{ => pattern/usefulness}/guards-not-exhaustive.rs | 0 .../irrefutable-exhaustive-integer-binding.rs | 0 src/test/ui/{ => pattern/usefulness}/irrefutable-unit.rs | 0 .../ui/{check_match => pattern/usefulness}/issue-35609.rs | 0 .../{check_match => pattern/usefulness}/issue-35609.stderr | 0 .../ui/{check_match => pattern/usefulness}/issue-43253.rs | 0 .../{check_match => pattern/usefulness}/issue-43253.stderr | 0 .../usefulness/match-arm-statics-2.rs} | 0 .../usefulness/match-arm-statics-2.stderr} | 6 +++--- .../ui/{match => pattern/usefulness}/match-arm-statics.rs | 0 .../{match => pattern/usefulness}/match-arm-statics.stderr | 0 .../usefulness}/match-byte-array-patterns-2.rs | 0 .../usefulness}/match-byte-array-patterns-2.stderr | 0 .../usefulness}/match-byte-array-patterns.rs | 0 .../usefulness}/match-byte-array-patterns.stderr | 0 .../{match => pattern/usefulness}/match-non-exhaustive.rs | 0 .../usefulness}/match-non-exhaustive.stderr | 0 .../{match => pattern/usefulness}/match-privately-empty.rs | 0 .../usefulness}/match-privately-empty.stderr | 0 .../usefulness}/match-range-fail-dominate.rs | 0 .../usefulness}/match-range-fail-dominate.stderr | 0 src/test/ui/{match => pattern/usefulness}/match-ref-ice.rs | 0 .../ui/{match => pattern/usefulness}/match-ref-ice.stderr | 0 .../{match => pattern/usefulness}/match-slice-patterns.rs | 0 .../usefulness}/match-slice-patterns.stderr | 0 .../ui/{match => pattern/usefulness}/match-vec-fixed.rs | 0 .../ui/{match => pattern/usefulness}/match-vec-fixed.stderr | 0 .../{match => pattern/usefulness}/match-vec-unreachable.rs | 0 .../usefulness}/match-vec-unreachable.stderr | 0 .../usefulness}/nested-exhaustive-match.rs | 0 .../usefulness}/non-exhaustive-defined-here.rs | 0 .../usefulness}/non-exhaustive-defined-here.stderr | 0 .../usefulness}/non-exhaustive-float-range-match.rs | 0 .../usefulness}/non-exhaustive-float-range-match.stderr | 0 .../usefulness}/non-exhaustive-match-nested.rs | 0 .../usefulness}/non-exhaustive-match-nested.stderr | 0 .../usefulness}/non-exhaustive-match.rs | 0 .../usefulness}/non-exhaustive-match.stderr | 0 .../usefulness}/non-exhaustive-pattern-witness.rs | 0 .../usefulness}/non-exhaustive-pattern-witness.stderr | 0 .../ui/{ => pattern/usefulness}/refutable-pattern-errors.rs | 0 .../usefulness}/refutable-pattern-errors.stderr | 0 .../{ => pattern/usefulness}/refutable-pattern-in-fn-arg.rs | 0 .../usefulness}/refutable-pattern-in-fn-arg.stderr | 0 .../{ => usefulness}/slice-pattern-exhaustiveness.rs | 0 .../{ => usefulness}/slice-pattern-exhaustiveness.stderr | 0 .../usefulness}/struct-like-enum-nonexhaustive.rs | 0 .../usefulness}/struct-like-enum-nonexhaustive.stderr | 0 .../usefulness}/struct-pattern-match-useless.rs | 0 .../usefulness}/struct-pattern-match-useless.stderr | 0 .../usefulness}/tuple-struct-nonexhaustive.rs | 0 .../usefulness}/tuple-struct-nonexhaustive.stderr | 0 57 files changed, 6 insertions(+), 3 deletions(-) rename src/test/ui/{uninhabited => pattern/usefulness}/always-inhabited-union-ref.rs (100%) rename src/test/ui/{uninhabited => pattern/usefulness}/always-inhabited-union-ref.stderr (100%) rename src/test/ui/{ => pattern/usefulness}/exhaustive_integer_patterns.rs (100%) rename src/test/ui/{ => pattern/usefulness}/exhaustive_integer_patterns.stderr (100%) rename src/test/ui/{ => pattern/usefulness}/guards-not-exhaustive.rs (100%) rename src/test/ui/pattern/{ => usefulness}/irrefutable-exhaustive-integer-binding.rs (100%) rename src/test/ui/{ => pattern/usefulness}/irrefutable-unit.rs (100%) rename src/test/ui/{check_match => pattern/usefulness}/issue-35609.rs (100%) rename src/test/ui/{check_match => pattern/usefulness}/issue-35609.stderr (100%) rename src/test/ui/{check_match => pattern/usefulness}/issue-43253.rs (100%) rename src/test/ui/{check_match => pattern/usefulness}/issue-43253.stderr (100%) rename src/test/ui/{match/match-argm-statics-2.rs => pattern/usefulness/match-arm-statics-2.rs} (100%) rename src/test/ui/{match/match-argm-statics-2.stderr => pattern/usefulness/match-arm-statics-2.stderr} (90%) rename src/test/ui/{match => pattern/usefulness}/match-arm-statics.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-arm-statics.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-byte-array-patterns-2.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-byte-array-patterns-2.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-byte-array-patterns.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-byte-array-patterns.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-non-exhaustive.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-non-exhaustive.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-privately-empty.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-privately-empty.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-range-fail-dominate.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-range-fail-dominate.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-ref-ice.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-ref-ice.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-slice-patterns.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-slice-patterns.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-vec-fixed.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-vec-fixed.stderr (100%) rename src/test/ui/{match => pattern/usefulness}/match-vec-unreachable.rs (100%) rename src/test/ui/{match => pattern/usefulness}/match-vec-unreachable.stderr (100%) rename src/test/ui/{binding => pattern/usefulness}/nested-exhaustive-match.rs (100%) rename src/test/ui/{match => pattern/usefulness}/non-exhaustive-defined-here.rs (100%) rename src/test/ui/{match => pattern/usefulness}/non-exhaustive-defined-here.stderr (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-float-range-match.rs (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-float-range-match.stderr (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-match-nested.rs (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-match-nested.stderr (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-match.rs (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-match.stderr (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-pattern-witness.rs (100%) rename src/test/ui/{non-exhaustive => pattern/usefulness}/non-exhaustive-pattern-witness.stderr (100%) rename src/test/ui/{ => pattern/usefulness}/refutable-pattern-errors.rs (100%) rename src/test/ui/{ => pattern/usefulness}/refutable-pattern-errors.stderr (100%) rename src/test/ui/{ => pattern/usefulness}/refutable-pattern-in-fn-arg.rs (100%) rename src/test/ui/{ => pattern/usefulness}/refutable-pattern-in-fn-arg.stderr (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-exhaustiveness.rs (100%) rename src/test/ui/pattern/{ => usefulness}/slice-pattern-exhaustiveness.stderr (100%) rename src/test/ui/{structs => pattern/usefulness}/struct-like-enum-nonexhaustive.rs (100%) rename src/test/ui/{structs => pattern/usefulness}/struct-like-enum-nonexhaustive.stderr (100%) rename src/test/ui/{structs => pattern/usefulness}/struct-pattern-match-useless.rs (100%) rename src/test/ui/{structs => pattern/usefulness}/struct-pattern-match-useless.stderr (100%) rename src/test/ui/{tuple => pattern/usefulness}/tuple-struct-nonexhaustive.rs (100%) rename src/test/ui/{tuple => pattern/usefulness}/tuple-struct-nonexhaustive.stderr (100%) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 495edc8c85186..b2cb79f6da5b1 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1,3 +1,6 @@ +/// Note: most tests relevant to this file can be found (at the time of writing) +/// in src/tests/ui/pattern/usefulness. +/// /// # Introduction /// /// This file includes the logic for exhaustiveness and usefulness checking for diff --git a/src/test/ui/uninhabited/always-inhabited-union-ref.rs b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs similarity index 100% rename from src/test/ui/uninhabited/always-inhabited-union-ref.rs rename to src/test/ui/pattern/usefulness/always-inhabited-union-ref.rs diff --git a/src/test/ui/uninhabited/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr similarity index 100% rename from src/test/ui/uninhabited/always-inhabited-union-ref.stderr rename to src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr diff --git a/src/test/ui/exhaustive_integer_patterns.rs b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.rs similarity index 100% rename from src/test/ui/exhaustive_integer_patterns.rs rename to src/test/ui/pattern/usefulness/exhaustive_integer_patterns.rs diff --git a/src/test/ui/exhaustive_integer_patterns.stderr b/src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr similarity index 100% rename from src/test/ui/exhaustive_integer_patterns.stderr rename to src/test/ui/pattern/usefulness/exhaustive_integer_patterns.stderr diff --git a/src/test/ui/guards-not-exhaustive.rs b/src/test/ui/pattern/usefulness/guards-not-exhaustive.rs similarity index 100% rename from src/test/ui/guards-not-exhaustive.rs rename to src/test/ui/pattern/usefulness/guards-not-exhaustive.rs diff --git a/src/test/ui/pattern/irrefutable-exhaustive-integer-binding.rs b/src/test/ui/pattern/usefulness/irrefutable-exhaustive-integer-binding.rs similarity index 100% rename from src/test/ui/pattern/irrefutable-exhaustive-integer-binding.rs rename to src/test/ui/pattern/usefulness/irrefutable-exhaustive-integer-binding.rs diff --git a/src/test/ui/irrefutable-unit.rs b/src/test/ui/pattern/usefulness/irrefutable-unit.rs similarity index 100% rename from src/test/ui/irrefutable-unit.rs rename to src/test/ui/pattern/usefulness/irrefutable-unit.rs diff --git a/src/test/ui/check_match/issue-35609.rs b/src/test/ui/pattern/usefulness/issue-35609.rs similarity index 100% rename from src/test/ui/check_match/issue-35609.rs rename to src/test/ui/pattern/usefulness/issue-35609.rs diff --git a/src/test/ui/check_match/issue-35609.stderr b/src/test/ui/pattern/usefulness/issue-35609.stderr similarity index 100% rename from src/test/ui/check_match/issue-35609.stderr rename to src/test/ui/pattern/usefulness/issue-35609.stderr diff --git a/src/test/ui/check_match/issue-43253.rs b/src/test/ui/pattern/usefulness/issue-43253.rs similarity index 100% rename from src/test/ui/check_match/issue-43253.rs rename to src/test/ui/pattern/usefulness/issue-43253.rs diff --git a/src/test/ui/check_match/issue-43253.stderr b/src/test/ui/pattern/usefulness/issue-43253.stderr similarity index 100% rename from src/test/ui/check_match/issue-43253.stderr rename to src/test/ui/pattern/usefulness/issue-43253.stderr diff --git a/src/test/ui/match/match-argm-statics-2.rs b/src/test/ui/pattern/usefulness/match-arm-statics-2.rs similarity index 100% rename from src/test/ui/match/match-argm-statics-2.rs rename to src/test/ui/pattern/usefulness/match-arm-statics-2.rs diff --git a/src/test/ui/match/match-argm-statics-2.stderr b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr similarity index 90% rename from src/test/ui/match/match-argm-statics-2.stderr rename to src/test/ui/pattern/usefulness/match-arm-statics-2.stderr index 8c54e030823af..8521e37d3fddc 100644 --- a/src/test/ui/match/match-argm-statics-2.stderr +++ b/src/test/ui/pattern/usefulness/match-arm-statics-2.stderr @@ -1,5 +1,5 @@ error[E0004]: non-exhaustive patterns: `(true, false)` not covered - --> $DIR/match-argm-statics-2.rs:17:11 + --> $DIR/match-arm-statics-2.rs:17:11 | LL | match (true, false) { | ^^^^^^^^^^^^^ pattern `(true, false)` not covered @@ -7,7 +7,7 @@ LL | match (true, false) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `Some(Some(West))` not covered - --> $DIR/match-argm-statics-2.rs:29:11 + --> $DIR/match-arm-statics-2.rs:29:11 | LL | match Some(Some(North)) { | ^^^^^^^^^^^^^^^^^ pattern `Some(Some(West))` not covered @@ -15,7 +15,7 @@ LL | match Some(Some(North)) { = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms error[E0004]: non-exhaustive patterns: `Foo { bar: Some(North), baz: NewBool(true) }` not covered - --> $DIR/match-argm-statics-2.rs:48:11 + --> $DIR/match-arm-statics-2.rs:48:11 | LL | / struct Foo { LL | | bar: Option, diff --git a/src/test/ui/match/match-arm-statics.rs b/src/test/ui/pattern/usefulness/match-arm-statics.rs similarity index 100% rename from src/test/ui/match/match-arm-statics.rs rename to src/test/ui/pattern/usefulness/match-arm-statics.rs diff --git a/src/test/ui/match/match-arm-statics.stderr b/src/test/ui/pattern/usefulness/match-arm-statics.stderr similarity index 100% rename from src/test/ui/match/match-arm-statics.stderr rename to src/test/ui/pattern/usefulness/match-arm-statics.stderr diff --git a/src/test/ui/match/match-byte-array-patterns-2.rs b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.rs similarity index 100% rename from src/test/ui/match/match-byte-array-patterns-2.rs rename to src/test/ui/pattern/usefulness/match-byte-array-patterns-2.rs diff --git a/src/test/ui/match/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr similarity index 100% rename from src/test/ui/match/match-byte-array-patterns-2.stderr rename to src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr diff --git a/src/test/ui/match/match-byte-array-patterns.rs b/src/test/ui/pattern/usefulness/match-byte-array-patterns.rs similarity index 100% rename from src/test/ui/match/match-byte-array-patterns.rs rename to src/test/ui/pattern/usefulness/match-byte-array-patterns.rs diff --git a/src/test/ui/match/match-byte-array-patterns.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns.stderr similarity index 100% rename from src/test/ui/match/match-byte-array-patterns.stderr rename to src/test/ui/pattern/usefulness/match-byte-array-patterns.stderr diff --git a/src/test/ui/match/match-non-exhaustive.rs b/src/test/ui/pattern/usefulness/match-non-exhaustive.rs similarity index 100% rename from src/test/ui/match/match-non-exhaustive.rs rename to src/test/ui/pattern/usefulness/match-non-exhaustive.rs diff --git a/src/test/ui/match/match-non-exhaustive.stderr b/src/test/ui/pattern/usefulness/match-non-exhaustive.stderr similarity index 100% rename from src/test/ui/match/match-non-exhaustive.stderr rename to src/test/ui/pattern/usefulness/match-non-exhaustive.stderr diff --git a/src/test/ui/match/match-privately-empty.rs b/src/test/ui/pattern/usefulness/match-privately-empty.rs similarity index 100% rename from src/test/ui/match/match-privately-empty.rs rename to src/test/ui/pattern/usefulness/match-privately-empty.rs diff --git a/src/test/ui/match/match-privately-empty.stderr b/src/test/ui/pattern/usefulness/match-privately-empty.stderr similarity index 100% rename from src/test/ui/match/match-privately-empty.stderr rename to src/test/ui/pattern/usefulness/match-privately-empty.stderr diff --git a/src/test/ui/match/match-range-fail-dominate.rs b/src/test/ui/pattern/usefulness/match-range-fail-dominate.rs similarity index 100% rename from src/test/ui/match/match-range-fail-dominate.rs rename to src/test/ui/pattern/usefulness/match-range-fail-dominate.rs diff --git a/src/test/ui/match/match-range-fail-dominate.stderr b/src/test/ui/pattern/usefulness/match-range-fail-dominate.stderr similarity index 100% rename from src/test/ui/match/match-range-fail-dominate.stderr rename to src/test/ui/pattern/usefulness/match-range-fail-dominate.stderr diff --git a/src/test/ui/match/match-ref-ice.rs b/src/test/ui/pattern/usefulness/match-ref-ice.rs similarity index 100% rename from src/test/ui/match/match-ref-ice.rs rename to src/test/ui/pattern/usefulness/match-ref-ice.rs diff --git a/src/test/ui/match/match-ref-ice.stderr b/src/test/ui/pattern/usefulness/match-ref-ice.stderr similarity index 100% rename from src/test/ui/match/match-ref-ice.stderr rename to src/test/ui/pattern/usefulness/match-ref-ice.stderr diff --git a/src/test/ui/match/match-slice-patterns.rs b/src/test/ui/pattern/usefulness/match-slice-patterns.rs similarity index 100% rename from src/test/ui/match/match-slice-patterns.rs rename to src/test/ui/pattern/usefulness/match-slice-patterns.rs diff --git a/src/test/ui/match/match-slice-patterns.stderr b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr similarity index 100% rename from src/test/ui/match/match-slice-patterns.stderr rename to src/test/ui/pattern/usefulness/match-slice-patterns.stderr diff --git a/src/test/ui/match/match-vec-fixed.rs b/src/test/ui/pattern/usefulness/match-vec-fixed.rs similarity index 100% rename from src/test/ui/match/match-vec-fixed.rs rename to src/test/ui/pattern/usefulness/match-vec-fixed.rs diff --git a/src/test/ui/match/match-vec-fixed.stderr b/src/test/ui/pattern/usefulness/match-vec-fixed.stderr similarity index 100% rename from src/test/ui/match/match-vec-fixed.stderr rename to src/test/ui/pattern/usefulness/match-vec-fixed.stderr diff --git a/src/test/ui/match/match-vec-unreachable.rs b/src/test/ui/pattern/usefulness/match-vec-unreachable.rs similarity index 100% rename from src/test/ui/match/match-vec-unreachable.rs rename to src/test/ui/pattern/usefulness/match-vec-unreachable.rs diff --git a/src/test/ui/match/match-vec-unreachable.stderr b/src/test/ui/pattern/usefulness/match-vec-unreachable.stderr similarity index 100% rename from src/test/ui/match/match-vec-unreachable.stderr rename to src/test/ui/pattern/usefulness/match-vec-unreachable.stderr diff --git a/src/test/ui/binding/nested-exhaustive-match.rs b/src/test/ui/pattern/usefulness/nested-exhaustive-match.rs similarity index 100% rename from src/test/ui/binding/nested-exhaustive-match.rs rename to src/test/ui/pattern/usefulness/nested-exhaustive-match.rs diff --git a/src/test/ui/match/non-exhaustive-defined-here.rs b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.rs similarity index 100% rename from src/test/ui/match/non-exhaustive-defined-here.rs rename to src/test/ui/pattern/usefulness/non-exhaustive-defined-here.rs diff --git a/src/test/ui/match/non-exhaustive-defined-here.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr similarity index 100% rename from src/test/ui/match/non-exhaustive-defined-here.stderr rename to src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr diff --git a/src/test/ui/non-exhaustive/non-exhaustive-float-range-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.rs similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-float-range-match.rs rename to src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.rs diff --git a/src/test/ui/non-exhaustive/non-exhaustive-float-range-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.stderr similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-float-range-match.stderr rename to src/test/ui/pattern/usefulness/non-exhaustive-float-range-match.stderr diff --git a/src/test/ui/non-exhaustive/non-exhaustive-match-nested.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.rs similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-match-nested.rs rename to src/test/ui/pattern/usefulness/non-exhaustive-match-nested.rs diff --git a/src/test/ui/non-exhaustive/non-exhaustive-match-nested.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-match-nested.stderr rename to src/test/ui/pattern/usefulness/non-exhaustive-match-nested.stderr diff --git a/src/test/ui/non-exhaustive/non-exhaustive-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-match.rs rename to src/test/ui/pattern/usefulness/non-exhaustive-match.rs diff --git a/src/test/ui/non-exhaustive/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-match.stderr rename to src/test/ui/pattern/usefulness/non-exhaustive-match.stderr diff --git a/src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.rs b/src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.rs rename to src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.rs diff --git a/src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr similarity index 100% rename from src/test/ui/non-exhaustive/non-exhaustive-pattern-witness.stderr rename to src/test/ui/pattern/usefulness/non-exhaustive-pattern-witness.stderr diff --git a/src/test/ui/refutable-pattern-errors.rs b/src/test/ui/pattern/usefulness/refutable-pattern-errors.rs similarity index 100% rename from src/test/ui/refutable-pattern-errors.rs rename to src/test/ui/pattern/usefulness/refutable-pattern-errors.rs diff --git a/src/test/ui/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr similarity index 100% rename from src/test/ui/refutable-pattern-errors.stderr rename to src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr diff --git a/src/test/ui/refutable-pattern-in-fn-arg.rs b/src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs similarity index 100% rename from src/test/ui/refutable-pattern-in-fn-arg.rs rename to src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.rs diff --git a/src/test/ui/refutable-pattern-in-fn-arg.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr similarity index 100% rename from src/test/ui/refutable-pattern-in-fn-arg.stderr rename to src/test/ui/pattern/usefulness/refutable-pattern-in-fn-arg.stderr diff --git a/src/test/ui/pattern/slice-pattern-exhaustiveness.rs b/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.rs similarity index 100% rename from src/test/ui/pattern/slice-pattern-exhaustiveness.rs rename to src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.rs diff --git a/src/test/ui/pattern/slice-pattern-exhaustiveness.stderr b/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.stderr similarity index 100% rename from src/test/ui/pattern/slice-pattern-exhaustiveness.stderr rename to src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.stderr diff --git a/src/test/ui/structs/struct-like-enum-nonexhaustive.rs b/src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs similarity index 100% rename from src/test/ui/structs/struct-like-enum-nonexhaustive.rs rename to src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.rs diff --git a/src/test/ui/structs/struct-like-enum-nonexhaustive.stderr b/src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr similarity index 100% rename from src/test/ui/structs/struct-like-enum-nonexhaustive.stderr rename to src/test/ui/pattern/usefulness/struct-like-enum-nonexhaustive.stderr diff --git a/src/test/ui/structs/struct-pattern-match-useless.rs b/src/test/ui/pattern/usefulness/struct-pattern-match-useless.rs similarity index 100% rename from src/test/ui/structs/struct-pattern-match-useless.rs rename to src/test/ui/pattern/usefulness/struct-pattern-match-useless.rs diff --git a/src/test/ui/structs/struct-pattern-match-useless.stderr b/src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr similarity index 100% rename from src/test/ui/structs/struct-pattern-match-useless.stderr rename to src/test/ui/pattern/usefulness/struct-pattern-match-useless.stderr diff --git a/src/test/ui/tuple/tuple-struct-nonexhaustive.rs b/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs similarity index 100% rename from src/test/ui/tuple/tuple-struct-nonexhaustive.rs rename to src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.rs diff --git a/src/test/ui/tuple/tuple-struct-nonexhaustive.stderr b/src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr similarity index 100% rename from src/test/ui/tuple/tuple-struct-nonexhaustive.stderr rename to src/test/ui/pattern/usefulness/tuple-struct-nonexhaustive.stderr From a91cdd4efa98f0b5fc07e4531a657da10c89bb1e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 30 Sep 2019 23:54:14 +0200 Subject: [PATCH 34/87] MatchCheckCtxt::byte_array_map is unused since 7df1d9f6564cafca3758e5d629920c02df526989 --- src/librustc_mir/hair/pattern/_match.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index b2cb79f6da5b1..b2bf6cdc214ed 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -200,7 +200,6 @@ use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; -use rustc_data_structures::fx::FxHashMap; use rustc_index::vec::Idx; use super::{compare_const_vals, PatternFoldable, PatternFolder}; @@ -488,7 +487,6 @@ pub struct MatchCheckCtxt<'a, 'tcx> { pub module: DefId, param_env: ty::ParamEnv<'tcx>, pub pattern_arena: &'a TypedArena>, - pub byte_array_map: FxHashMap<*const Pat<'tcx>, Vec<&'a Pat<'tcx>>>, } impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { @@ -503,13 +501,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { { let pattern_arena = TypedArena::default(); - f(MatchCheckCtxt { - tcx, - param_env, - module, - pattern_arena: &pattern_arena, - byte_array_map: FxHashMap::default(), - }) + f(MatchCheckCtxt { tcx, param_env, module, pattern_arena: &pattern_arena }) } fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool { From 8e89f3c1d178b8942594cc96c50be4029f37888a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 30 Sep 2019 23:58:28 +0200 Subject: [PATCH 35/87] No need to mutate MatchCheckCtxt anymore --- src/librustc_mir/hair/pattern/_match.rs | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index b2bf6cdc214ed..892325142bf3a 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -344,7 +344,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize<'a, 'p2>( &self, - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], ) -> Option> @@ -407,7 +407,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize<'a, 'p2>( &self, - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], ) -> Matrix<'p2, 'tcx> @@ -1187,7 +1187,7 @@ fn all_constructors<'a, 'tcx>( ctors } -fn max_slice_length<'p, 'a, 'tcx, I>(cx: &mut MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 +fn max_slice_length<'p, 'a, 'tcx, I>(cx: &MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 where I: Iterator>, 'tcx: 'p, @@ -1620,7 +1620,7 @@ impl<'tcx> PartialEq for MissingConstructors<'tcx> { /// checking (if a wildcard pattern is useful in relation to a matrix, the /// matrix isn't exhaustive). pub fn is_useful<'p, 'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'_, 'tcx>, witness_preference: WitnessPreference, @@ -1686,13 +1686,9 @@ pub fn is_useful<'p, 'a, 'tcx>( .collect(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); - let constructors: Vec<_> = v_constructors + v_constructors .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &matrix_head_ctors)) - .collect(); - - constructors - .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) @@ -1701,7 +1697,7 @@ pub fn is_useful<'p, 'a, 'tcx>( /// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied /// to the specialised version of both the pattern matrix `P` and the new pattern `q`. fn is_useful_specialized<'p, 'a, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, @@ -1927,7 +1923,7 @@ fn patterns_for_variant<'p, 'tcx>( /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( - cx: &mut MatchCheckCtxt<'a, 'tcx>, + cx: &MatchCheckCtxt<'a, 'tcx>, mut pat: &'p2 Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], From 9ae4c521f5e8bb1ddb55d6e6e2e9e17c58f04cd8 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 00:38:24 +0200 Subject: [PATCH 36/87] Add comment about non_exhaustive feature --- src/librustc_mir/hair/pattern/_match.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 892325142bf3a..f17b186605ac0 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1,5 +1,6 @@ /// Note: most tests relevant to this file can be found (at the time of writing) -/// in src/tests/ui/pattern/usefulness. +/// in src/tests/ui/pattern/usefulness. Also look out for rfc2008 (feature +/// non_exhaustive) tests. /// /// # Introduction /// @@ -1676,6 +1677,7 @@ pub fn is_useful<'p, 'a, 'tcx>( && !v_constructors.iter().any(|ctor| ctor.is_wildcard()) { debug!("is_useful - shortcut because declared non-exhaustive"); + // FIXME(#65157) return Useful; } From b3de6fd6dcb1b7643ec9afd95bc22f690af59846 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 01:00:03 +0200 Subject: [PATCH 37/87] Use Wildcard instead of MissingConstructors when relevant This both simplifies the missing constructors codepath and exactly matches what we want for witness construction. So this behaviour of witness construction isn't an ad-hoc tweak anymore, it also makes semantic sense. --- src/librustc_mir/hair/pattern/_match.rs | 137 ++++++++---------------- 1 file changed, 46 insertions(+), 91 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f17b186605ac0..de15904b8ab61 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -559,8 +559,8 @@ enum Constructor<'tcx> { Wildcard, /// List of constructors that were _not_ present in the first column /// of the matrix when encountering a wildcard. The contained list must - /// be nonempty unless the type is non-exhaustive. - /// This is only used in the output of metaconstructor splitting. + /// be nonempty. + /// This is only used in the output of splitting the wildcard metaconstructor. MissingConstructors(MissingConstructors<'tcx>), } @@ -597,6 +597,8 @@ impl<'tcx> Constructor<'tcx> { /// Split a metaconstructor into equivalence classes of constructors that behave the same /// for the given matrix. See description of the algorithm for details. + /// Note: If the type is uninhabited and we're not in the privately_empty case, then this will + /// return an empty list even if the constructor was a wildcard. fn split_meta_constructor( self, cx: &MatchCheckCtxt<'_, 'tcx>, @@ -735,27 +737,39 @@ impl<'tcx> Constructor<'tcx> { // Missing constructors are those that are not matched by any // non-wildcard patterns in the current column. - let missing_ctors = MissingConstructors::new( - cx.tcx, - cx.param_env, - all_ctors, - head_ctors.clone(), - is_non_exhaustive, - ); + let missing_ctors = + MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, head_ctors.clone()); debug!( "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", missing_ctors.is_empty(), is_non_exhaustive, ); - if !missing_ctors.is_empty() || is_non_exhaustive { - smallvec![MissingConstructors(missing_ctors)] + // If there are some missing constructors, we only need to specialize relative + // to them and we can ignore the other ones. Otherwise, we have to try all + // existing constructors one-by-one. + if is_non_exhaustive { + // We pretend the type has an additional `_` constructor, that counts as a missing + // constructor. So we return that constructor. + smallvec![Wildcard] + } else if !missing_ctors.is_empty() { + if head_ctors.is_empty() { + // If head_ctors is empty, then all constructors of the type behave the same + // so we can keep the Wildcard metaconstructor. + smallvec![Wildcard] + } else { + // Otherwise, we have a set of missing constructors that is neither empty + // not equal to all_constructors. Since all missing constructors will behave + // the same (i.e. will be matched only by wildcards), we return a metaconstructor + // that contains all of them at once. + smallvec![MissingConstructors(missing_ctors)] + } } else { + // Here we know there are no missing constructors, so we have to try all existing + // constructors one-by-one. let (all_ctors, _) = missing_ctors.into_inner(); // Recursively split newly generated list of constructors. This list must not contain // any wildcards so we don't recurse infinitely. - // Note: If the type is uninhabited and we're not in the privately_empty case, then this will - // return an empty list. all_ctors .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, head_ctors)) @@ -905,68 +919,18 @@ impl<'tcx> Constructor<'tcx> { }), Wildcard => PatKind::Wild, MissingConstructors(missing_ctors) => { - // 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 missing_ctors.is_non_exhaustive() || missing_ctors.is_complete() { - // All constructors are unused. Add a wild pattern - // rather than each individual constructor. - 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(_)`. - return missing_ctors - .iter() - .flat_map(|ctor| ctor.apply_wildcards(cx, 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(_)`. + return missing_ctors + .iter() + .flat_map(|ctor| ctor.apply_wildcards(cx, ty)) + .collect(); } }; - smallvec!(Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }) + smallvec![Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }] } /// Like `apply`, but where all the subpatterns are wildcards `_`. @@ -1504,7 +1468,6 @@ struct MissingConstructors<'tcx> { param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, - is_non_exhaustive: bool, } type MissingConstructorsIter<'a, 'tcx, F> = @@ -1516,26 +1479,17 @@ impl<'tcx> MissingConstructors<'tcx> { param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, - is_non_exhaustive: bool, ) -> Self { - MissingConstructors { tcx, param_env, all_ctors, used_ctors, is_non_exhaustive } + MissingConstructors { tcx, param_env, all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { (self.all_ctors, self.used_ctors) } - fn is_non_exhaustive(&self) -> bool { - self.is_non_exhaustive - } 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 is_complete(&self) -> bool { - self.used_ctors.is_empty() - } /// Iterate over all_ctors \ used_ctors // Can't use impl Iterator because of lifetime shenanigans @@ -1581,11 +1535,7 @@ impl<'tcx> MissingConstructors<'tcx> { impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let ctors: Vec<_> = self.iter().collect(); - f.debug_struct("MissingConstructors") - .field("is_empty", &self.is_empty()) - .field("is_non_exhaustive", &self.is_non_exhaustive) - .field("ctors", &ctors) - .finish() + f.debug_tuple("MissingConstructors").field(&ctors).finish() } } @@ -1942,9 +1892,14 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( _ => None, }; } else if let Wildcard = constructor { - bug!( - "tried to specialize on the Wildcard meta-constructor; maybe you wanted to use MissingConstructors" - ) + // If we get here, either there were only wildcards in the first component of the + // matrix, or we are in a special non_exhaustive case where we pretend the type has + // an extra `_` constructor to prevent exhaustive matching. In both cases, all + // non-wildcard constructors should be discarded. + return match *pat.kind { + PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), + _ => None, + }; } match *pat.kind { From 4be3802fe40730560795267d8861cb3ca42a111e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 01:20:36 +0200 Subject: [PATCH 38/87] Make IntRange::subtract_from work on a single constructor --- src/librustc_mir/hair/pattern/_match.rs | 59 +++++++++++++------------ 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index de15904b8ab61..2b604f4270f67 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1413,37 +1413,37 @@ impl<'tcx> IntRange<'tcx> { } } - /// Returns a collection of ranges that spans the values covered by `ranges`, subtracted - /// by the values covered by `self`: i.e., `ranges \ self` (in set notation). + /// Returns a collection of ranges that spans the values covered by `ctor`, subtracted + /// by the values covered by `self`: i.e., `ctor \ self` (in set notation). fn subtract_from( - self, + &self, tcx: TyCtxt<'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 mut remaining_ranges = vec![]; + ctor: Constructor<'tcx>, + ) -> SmallVec<[Constructor<'tcx>; 2]> { + let range = match IntRange::from_ctor(tcx, param_env, &ctor) { + None => return smallvec![], + Some(int_range) => int_range.range, + }; + 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 { - // 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)); - } 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))); - } - 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)); - } + let (lo, hi) = (*self.range.start(), *self.range.end()); + let (range_lo, range_hi) = range.into_inner(); + let mut remaining_ranges = smallvec![]; + if lo > range_hi || range_lo > hi { + // The pattern doesn't intersect with the range at all, + // so the range remains untouched. + remaining_ranges.push(Self::range_to_ctor(tcx, ty, range_lo..=range_hi)); + } else { + if lo > range_lo { + // The pattern intersects an upper section of the + // range, so a lower section will remain. + remaining_ranges.push(Self::range_to_ctor(tcx, ty, range_lo..=(lo - 1))); + } + if hi < range_hi { + // The pattern intersects a lower section of the + // range, so an upper section will remain. + remaining_ranges.push(Self::range_to_ctor(tcx, ty, (hi + 1)..=range_hi)); } } remaining_ranges @@ -1512,7 +1512,10 @@ impl<'tcx> MissingConstructors<'tcx> { { // 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); + refined_ctors = refined_ctors + .into_iter() + .flat_map(|ctor| interval.subtract_from(self.tcx, self.param_env, ctor)) + .collect(); } // If the constructor patterns that have been considered so far From d12b1722d3979f147ce00ccbcf9f203f56f9683a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 01:43:13 +0200 Subject: [PATCH 39/87] Factor out constructor subtraction --- src/librustc_mir/hair/pattern/_match.rs | 81 ++++++++++++++----------- 1 file changed, 45 insertions(+), 36 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 2b604f4270f67..79f1fc5929d35 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -595,10 +595,10 @@ impl<'tcx> Constructor<'tcx> { } } - /// Split a metaconstructor into equivalence classes of constructors that behave the same + /// Split a constructor into equivalence classes of constructors that behave the same /// for the given matrix. See description of the algorithm for details. - /// Note: If the type is uninhabited and we're not in the privately_empty case, then this will - /// return an empty list even if the constructor was a wildcard. + /// Note: We can rely on this returning an empty list if the type is uninhabited and + /// we're not in the privately_empty case. fn split_meta_constructor( self, cx: &MatchCheckCtxt<'_, 'tcx>, @@ -780,6 +780,41 @@ impl<'tcx> Constructor<'tcx> { } } + /// Returns a collection of constructors that spans the constructors covered by `self`, subtracted + /// by the constructors covered by `head_ctors`: i.e., `self \ head_ctors` (in set notation). + fn subtract_meta_constructor( + self, + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + used_ctors: &Vec>, + ) -> SmallVec<[Constructor<'tcx>; 1]> { + debug!("subtract_meta_constructor {:?}", self); + let mut remaining_ctors = smallvec![self.clone()]; + // For each used ctor, subtract from the current set of constructors. + for used_ctor in used_ctors { + if used_ctor == &self { + // If a constructor appears in a `match` arm, we can + // eliminate it straight away. + remaining_ctors = smallvec![] + } 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. + remaining_ctors = remaining_ctors + .into_iter() + .flat_map(|ctor| interval.subtract_from(tcx, param_env, ctor)) + .collect(); + } + + // If the constructors that have been considered so far already cover + // the entire range of `self`, no need to look at more constructors. + if remaining_ctors.is_empty() { + break; + } + } + + remaining_ctors + } + /// This returns one wildcard pattern for each argument to this constructor. fn wildcard_subpatterns<'a>( &self, @@ -1470,8 +1505,11 @@ struct MissingConstructors<'tcx> { used_ctors: Vec>, } -type MissingConstructorsIter<'a, 'tcx, F> = - std::iter::FlatMap>, Vec>, F>; +type MissingConstructorsIter<'a, 'tcx, F> = std::iter::FlatMap< + std::slice::Iter<'a, Constructor<'tcx>>, + SmallVec<[Constructor<'tcx>; 1]>, + F, +>; impl<'tcx> MissingConstructors<'tcx> { fn new( @@ -1498,39 +1536,10 @@ impl<'tcx> MissingConstructors<'tcx> { ) -> MissingConstructorsIter< 'a, 'tcx, - impl FnMut(&'a Constructor<'tcx>) -> Vec>, + impl FnMut(&'a Constructor<'tcx>) -> SmallVec<[Constructor<'tcx>; 1]>, > { 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 = refined_ctors - .into_iter() - .flat_map(|ctor| interval.subtract_from(self.tcx, self.param_env, ctor)) - .collect(); - } - - // 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.clone().subtract_meta_constructor(self.tcx, self.param_env, &self.used_ctors) }) } } From 26871404646860f6113d2827348331cb5542508d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 22:39:49 +0200 Subject: [PATCH 40/87] subtract_meta_constructor: match on constructor first --- src/librustc_mir/hair/pattern/_match.rs | 63 +++++++++++++++++-------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 79f1fc5929d35..f0ce7b573cf0d 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -575,6 +575,9 @@ impl<'tcx> Constructor<'tcx> { fn is_wildcard(&self) -> bool { match self { Wildcard => true, + MissingConstructors(_) => bug!( + "Not sure if MissingConstructors should count as a wildcard. Shouldn't happen anyways." + ), _ => false, } } @@ -606,6 +609,8 @@ impl<'tcx> Constructor<'tcx> { head_ctors: &Vec>, ) -> SmallVec<[Constructor<'tcx>; 1]> { debug!("split_meta_constructor {:?}", self); + assert!(!head_ctors.iter().any(|c| c.is_wildcard())); + match &self { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], @@ -789,30 +794,48 @@ impl<'tcx> Constructor<'tcx> { used_ctors: &Vec>, ) -> SmallVec<[Constructor<'tcx>; 1]> { debug!("subtract_meta_constructor {:?}", self); - let mut remaining_ctors = smallvec![self.clone()]; - // For each used ctor, subtract from the current set of constructors. - for used_ctor in used_ctors { - if used_ctor == &self { - // If a constructor appears in a `match` arm, we can - // eliminate it straight away. - remaining_ctors = smallvec![] - } 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. - remaining_ctors = remaining_ctors - .into_iter() - .flat_map(|ctor| interval.subtract_from(tcx, param_env, ctor)) - .collect(); + assert!(!used_ctors.iter().any(|c| c.is_wildcard())); + + match &self { + // Those constructors can't match a non-wildcard metaconstructor, so we're fine + // just comparing for equality. + Single | Variant(_) | FixedLenSlice(_) => { + if used_ctors.iter().any(|c| c == &self) { + smallvec![] + } else { + smallvec![self] + } } + ConstantRange(..) | ConstantValue(..) => { + let mut remaining_ctors = smallvec![self]; + + // For each used ctor, subtract from the current set of constructors. + for used_ctor in used_ctors { + remaining_ctors = remaining_ctors + .into_iter() + .filter(|ctor| ctor != used_ctor) + .flat_map(|ctor| -> SmallVec<[Constructor<'tcx>; 2]> { + if let Some(interval) = IntRange::from_ctor(tcx, param_env, used_ctor) { + interval.subtract_from(tcx, param_env, ctor) + } else { + smallvec![ctor] + } + }) + .collect(); - // If the constructors that have been considered so far already cover - // the entire range of `self`, no need to look at more constructors. - if remaining_ctors.is_empty() { - break; + // If the constructors that have been considered so far already cover + // the entire range of `self`, no need to look at more constructors. + if remaining_ctors.is_empty() { + break; + } + } + + remaining_ctors + } + Wildcard | MissingConstructors(_) => { + bug!("shouldn't try to subtract constructor {:?}", self) } } - - remaining_ctors } /// This returns one wildcard pattern for each argument to this constructor. From 1f8d93c7435d3c324ca6f126f7b9209fb3deaf08 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Oct 2019 18:07:41 +0100 Subject: [PATCH 41/87] Extract constructor_intersects_pattern function This reverses changes from #65089, but I need this temporarily for rebase to be tractable. --- src/librustc_mir/hair/pattern/_match.rs | 54 ++++++++++++++----------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f0ce7b573cf0d..0bb620696b31e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1825,6 +1825,35 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) } } +fn constructor_intersects_pattern<'p, 'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ctor: &Constructor<'tcx>, + pat: &'p Pat<'tcx>, +) -> Option> { + if should_treat_range_exhaustively(tcx, ctor) { + match (IntRange::from_ctor(tcx, param_env, ctor), IntRange::from_pat(tcx, 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); + PatStack::default() + }), + _ => None, + } + } else { + // Fallback for non-ranges and ranges that involve + // floating-point numbers, which are not conveniently handled + // by `IntRange`. For these cases, the constructor may not be a + // range so intersection actually devolves into being covered + // by the pattern. + match constructor_covered_by_range(tcx, param_env, ctor, pat) { + Ok(true) => Some(PatStack::default()), + Ok(false) | Err(ErrorReported) => None, + } + } +} + fn constructor_covered_by_range<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -2016,30 +2045,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( // 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); - PatStack::default() - }), - _ => None, - } - } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // 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(PatStack::default()), - Ok(false) | Err(ErrorReported) => None, - } - } + constructor_intersects_pattern(cx.tcx, cx.param_env, constructor, pat) } PatKind::Array { ref prefix, ref slice, ref suffix } From 1b65bec9c2ef00dbe5fdc2edc1d484f438f6594d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 23:07:30 +0200 Subject: [PATCH 42/87] Inline constructor_covered_by_range and IntRange::from_pat --- src/librustc_mir/hair/pattern/_match.rs | 179 +++++++++++------------- 1 file changed, 80 insertions(+), 99 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 0bb620696b31e..7d23b0a899636 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1417,33 +1417,6 @@ impl<'tcx> IntRange<'tcx> { } } - fn from_pat( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - mut pat: &Pat<'tcx>, - ) -> Option> { - loop { - match pat.kind { - box PatKind::Constant { value } => { - return Self::from_const(tcx, param_env, value); - } - box PatKind::Range(PatRange { lo, hi, end }) => { - return Self::from_range( - tcx, - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), - &lo.ty, - &end, - ); - } - box PatKind::AscribeUserType { ref subpattern, .. } => { - pat = subpattern; - } - _ => return None, - } - } - } - // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it. fn signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> u128 { match ty.kind { @@ -1825,6 +1798,7 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) } } +/// Checks whether there exists any shared value in either `ctor` or `pat` by intersecting them. fn constructor_intersects_pattern<'p, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -1832,83 +1806,90 @@ fn constructor_intersects_pattern<'p, 'tcx>( pat: &'p Pat<'tcx>, ) -> Option> { if should_treat_range_exhaustively(tcx, ctor) { - match (IntRange::from_ctor(tcx, param_env, ctor), IntRange::from_pat(tcx, 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); - PatStack::default() - }), - _ => None, - } + let range = match *pat.kind { + PatKind::Constant { value } => ConstantValue(value), + PatKind::Range(PatRange { lo, hi, end }) => ConstantRange( + lo.eval_bits(tcx, param_env, lo.ty), + hi.eval_bits(tcx, param_env, hi.ty), + lo.ty, + end, + ), + _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), + }; + + let pat = IntRange::from_ctor(tcx, param_env, &range)?; + let ctor = IntRange::from_ctor(tcx, param_env, ctor)?; + ctor.intersection(&pat)?; + + 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); + Some(PatStack::default()) } else { - // Fallback for non-ranges and ranges that involve - // floating-point numbers, which are not conveniently handled - // by `IntRange`. For these cases, the constructor may not be a - // range so intersection actually devolves into being covered - // by the pattern. - match constructor_covered_by_range(tcx, param_env, ctor, pat) { - Ok(true) => Some(PatStack::default()), - Ok(false) | Err(ErrorReported) => None, + // Fallback for non-ranges and ranges that involve floating-point numbers, which are not + // conveniently handled by `IntRange`. For these cases, the constructor may not be a range + // so intersection actually devolves into being covered by the pattern. + let (from, to, end, ty) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), + _ => 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_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); + macro_rules! some_or_ok { + ($e:expr) => { + match $e { + Some(to) => to, + None => return None, // not char or int + } + }; } - } -} - -fn constructor_covered_by_range<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - pat: &Pat<'tcx>, -) -> Result { - let (from, to, end, ty) = match pat.kind { - box PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - box PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => 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_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); - macro_rules! some_or_ok { - ($e:expr) => { - match $e { - Some(to) => to, - None => return Ok(false), // not char or int + let result = match *ctor { + ConstantValue(value) => { + let to = some_or_ok!(cmp_to(value)); + 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); + 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); + Ok(some_or_ok!(cmp_from(ty::Const::from_bits( + tcx, + from, + ty::ParamEnv::empty().and(ty) + ))) && end) + } + Single => Ok(true), + _ => bug!(), }; - } - match *ctor { - ConstantValue(value) => { - let to = some_or_ok!(cmp_to(value)); - 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); - 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); - Ok(some_or_ok!(cmp_from(ty::Const::from_bits( - tcx, - from, - ty::ParamEnv::empty().and(ty) - ))) && end) + + match result { + Ok(true) => Some(PatStack::default()), + Ok(false) | Err(ErrorReported) => None, } - Single => Ok(true), - _ => bug!(), } } From 12bb3b71b7ab73873d0f40228d7e4eefa6c423c1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 23:08:47 +0200 Subject: [PATCH 43/87] Use question mark instead of custom macro --- src/librustc_mir/hair/pattern/_match.rs | 45 +++++-------------------- 1 file changed, 9 insertions(+), 36 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 7d23b0a899636..5918ba9aeba7f 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1832,64 +1832,37 @@ fn constructor_intersects_pattern<'p, 'tcx>( let (from, to, end, ty) = match *pat.kind { PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), - _ => bug!("`constructor_covered_by_range` called with {:?}", pat), + _ => bug!("`constructor_intersects_pattern` 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_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); - macro_rules! some_or_ok { - ($e:expr) => { - match $e { - Some(to) => to, - None => return None, // not char or int - } - }; - } let result = match *ctor { ConstantValue(value) => { - let to = some_or_ok!(cmp_to(value)); + let to = cmp_to(value)?; let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - Ok(some_or_ok!(cmp_from(value)) && end) + 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 to = 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) + 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 to = 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) + cmp_from(ty::Const::from_bits(tcx, from, ty::ParamEnv::empty().and(ty)))? && end } - Single => Ok(true), + Single => true, _ => bug!(), }; - match result { - Ok(true) => Some(PatStack::default()), - Ok(false) | Err(ErrorReported) => None, - } + if result { Some(PatStack::default()) } else { None } } } From 022c6206797101a8694a1497d418a7a992ceb296 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 1 Oct 2019 23:26:23 +0200 Subject: [PATCH 44/87] Regroup similar code paths --- src/librustc_mir/hair/pattern/_match.rs | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 5918ba9aeba7f..fedf8eca2916a 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1834,29 +1834,23 @@ fn constructor_intersects_pattern<'p, 'tcx>( PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), _ => bug!("`constructor_intersects_pattern` 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) - }; + trace!("constructor_intersects_pattern {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); + let cmp_from = |c_from| compare_const_vals(tcx, c_from, from, param_env, ty); let cmp_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); let result = match *ctor { ConstantValue(value) => { let to = cmp_to(value)?; + let from = cmp_from(value)?; let end = (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - cmp_from(value)? && end - } - ConstantRange(from, to, ty, RangeEnd::Included) => { - let to = cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty)))?; - let end = - (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - cmp_from(ty::Const::from_bits(tcx, from, ty::ParamEnv::empty().and(ty)))? && end + (from != Ordering::Less) && end } - ConstantRange(from, to, ty, RangeEnd::Excluded) => { + ConstantRange(from, to, ty, range_end) => { let to = cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty)))?; - let end = - (to == Ordering::Less) || (end == RangeEnd::Excluded && to == Ordering::Equal); - cmp_from(ty::Const::from_bits(tcx, from, ty::ParamEnv::empty().and(ty)))? && end + let from = + cmp_from(ty::Const::from_bits(tcx, from, ty::ParamEnv::empty().and(ty)))?; + let end = (to == Ordering::Less) || (end == range_end && to == Ordering::Equal); + (from != Ordering::Less) && end } Single => true, _ => bug!(), From 0ab764bff0bb427c08709a1132c5f7b1afe97b75 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 2 Oct 2019 15:22:58 +0200 Subject: [PATCH 45/87] Add a few more slice pattern usefulness tests --- .../slice-pattern-exhaustiveness.rs | 28 ----- .../slice-pattern-exhaustiveness.stderr | 11 -- .../ui/pattern/usefulness/slice-patterns.rs | 96 ++++++++++++++ .../pattern/usefulness/slice-patterns.stderr | 117 ++++++++++++++++++ 4 files changed, 213 insertions(+), 39 deletions(-) delete mode 100644 src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.rs delete mode 100644 src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.stderr create mode 100644 src/test/ui/pattern/usefulness/slice-patterns.rs create mode 100644 src/test/ui/pattern/usefulness/slice-patterns.stderr diff --git a/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.rs b/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.rs deleted file mode 100644 index 681e01cdb0554..0000000000000 --- a/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![feature(slice_patterns)] - -fn main() { - let s: [bool; 1] = [false; 1]; - match s { - [a] => {} - } - match s { - [a, ..] => {} - } - match s { - [true, ..] => {} - [.., false] => {} - } - - let s: [bool; 2] = [false; 2]; - match s { - [a, b] => {} - } - match s { - [a, ..] => {} - } - match s { - //~^ ERROR `[false, true]` not covered - [true, ..] => {} - [.., false] => {} - } -} diff --git a/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.stderr b/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.stderr deleted file mode 100644 index ae9695e521499..0000000000000 --- a/src/test/ui/pattern/usefulness/slice-pattern-exhaustiveness.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0004]: non-exhaustive patterns: `[false, true]` not covered - --> $DIR/slice-pattern-exhaustiveness.rs:23:11 - | -LL | match s { - | ^ pattern `[false, true]` not covered - | - = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs new file mode 100644 index 0000000000000..0493646ff3bba --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -0,0 +1,96 @@ +#![feature(slice_patterns)] +#![deny(unreachable_patterns)] + +fn main() { + let s: &[bool] = &[true; 0]; + let s0: &[bool; 0] = &[]; + let s1: &[bool; 1] = &[false; 1]; + let s2: &[bool; 2] = &[false; 2]; + let s3: &[bool; 3] = &[false; 3]; + + let [] = s0; + let [_] = s1; + let [_, _] = s2; + + let [..] = s; + let [..] = s0; + let [..] = s1; + let [..] = s2; + let [..] = s3; + + let [_, _, ..] = s2; + let [_, .., _] = s2; + let [.., _, _] = s2; + + match s1 { + [true, ..] => {} + [.., false] => {} + } + match s2 { + //~^ ERROR `&[false, true]` not covered + [true, ..] => {} + [.., false] => {} + } + match s3 { + //~^ ERROR `&[false, _, true]` not covered + [true, ..] => {} + [.., false] => {} + } + match s { + //~^ ERROR `&[false, true]` not covered + [] => {} + [true, ..] => {} + [.., false] => {} + } + + match s3 { + //~^ ERROR `&[false, _, _]` not covered + [true, .., true] => {} + } + match s { + //~^ ERROR `&[_]` not covered + [] => {} + } + match s { + //~^ ERROR `&[_, _]` not covered + [] => {} + [_] => {} + } + match s { + //~^ ERROR `&[false]` not covered + [] => {} + [true, ..] => {} + } + match s { + //~^ ERROR `&[false, _]` not covered + [] => {} + [_] => {} + [true, ..] => {} + } + match s { + //~^ ERROR `&[_, false]` not covered + [] => {} + [_] => {} + [.., true] => {} + } + + match s { + [true, ..] => {} + [true, ..] => {} //~ ERROR unreachable pattern + [true] => {} //~ ERROR unreachable pattern + [..] => {} + } + match s { + [.., true] => {} + [.., true] => {} //~ ERROR unreachable pattern + [true] => {} //~ ERROR unreachable pattern + [..] => {} + } + match s { + [false, .., true] => {} + [false, .., true] => {} //~ ERROR unreachable pattern + [false, true] => {} //~ ERROR unreachable pattern + [false] => {} + [..] => {} + } +} diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr new file mode 100644 index 0000000000000..18c73330fcdc1 --- /dev/null +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -0,0 +1,117 @@ +error[E0004]: non-exhaustive patterns: `&[false, true]` not covered + --> $DIR/slice-patterns.rs:29:11 + | +LL | match s2 { + | ^^ pattern `&[false, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, _, true]` not covered + --> $DIR/slice-patterns.rs:34:11 + | +LL | match s3 { + | ^^ pattern `&[false, _, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, true]` not covered + --> $DIR/slice-patterns.rs:39:11 + | +LL | match s { + | ^ pattern `&[false, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, _, _]` not covered + --> $DIR/slice-patterns.rs:46:11 + | +LL | match s3 { + | ^^ pattern `&[false, _, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[_]` not covered + --> $DIR/slice-patterns.rs:50:11 + | +LL | match s { + | ^ pattern `&[_]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[_, _]` not covered + --> $DIR/slice-patterns.rs:54:11 + | +LL | match s { + | ^ pattern `&[_, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false]` not covered + --> $DIR/slice-patterns.rs:59:11 + | +LL | match s { + | ^ pattern `&[false]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[false, _]` not covered + --> $DIR/slice-patterns.rs:64:11 + | +LL | match s { + | ^ pattern `&[false, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[_, false]` not covered + --> $DIR/slice-patterns.rs:70:11 + | +LL | match s { + | ^ pattern `&[_, false]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: unreachable pattern + --> $DIR/slice-patterns.rs:79:9 + | +LL | [true, ..] => {} + | ^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/slice-patterns.rs:2:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:80:9 + | +LL | [true] => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:85:9 + | +LL | [.., true] => {} + | ^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:86:9 + | +LL | [true] => {} + | ^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:91:9 + | +LL | [false, .., true] => {} + | ^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/slice-patterns.rs:92:9 + | +LL | [false, true] => {} + | ^^^^^^^^^^^^^ + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0004`. From 956838dabcb431d5043244c473dc9e6fc922c38a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 2 Oct 2019 15:24:55 +0200 Subject: [PATCH 46/87] Add variable-length slice metaconstructor --- src/librustc_mir/hair/pattern/_match.rs | 77 +++++++++++++++++++++---- 1 file changed, 67 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index fedf8eca2916a..0892e0b4dab3b 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -555,6 +555,8 @@ enum Constructor<'tcx> { // Meta-constructors /// Ranges of literal values (`2..=5` and `2..5`). ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), + /// Slice patterns. Captures any array constructor of length >= n. + VarLenSlice(u64), /// Wildcard metaconstructor. Wildcard, /// List of constructors that were _not_ present in the first column @@ -704,6 +706,7 @@ impl<'tcx> Constructor<'tcx> { .collect() } ConstantRange(..) => smallvec![self], + VarLenSlice(len) => (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect(), Wildcard => { let is_declared_nonexhaustive = !cx.is_local(pcx.ty) && cx.is_non_exhaustive_enum(pcx.ty); @@ -742,8 +745,13 @@ impl<'tcx> Constructor<'tcx> { // Missing constructors are those that are not matched by any // non-wildcard patterns in the current column. - let missing_ctors = - MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, head_ctors.clone()); + let missing_ctors = MissingConstructors::new( + pcx, + cx.tcx, + cx.param_env, + all_ctors, + head_ctors.clone(), + ); debug!( "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", missing_ctors.is_empty(), @@ -789,6 +797,7 @@ impl<'tcx> Constructor<'tcx> { /// by the constructors covered by `head_ctors`: i.e., `self \ head_ctors` (in set notation). fn subtract_meta_constructor( self, + pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, used_ctors: &Vec>, @@ -799,13 +808,52 @@ impl<'tcx> Constructor<'tcx> { match &self { // Those constructors can't match a non-wildcard metaconstructor, so we're fine // just comparing for equality. - Single | Variant(_) | FixedLenSlice(_) => { + Single | Variant(_) => { if used_ctors.iter().any(|c| c == &self) { smallvec![] } else { smallvec![self] } } + FixedLenSlice(_) | VarLenSlice(_) => { + let mut remaining_ctors = if let VarLenSlice(len) = &self { + (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() + } else { + smallvec![self] + }; + + // For each used ctor, subtract from the current set of constructors. + // Naming: we remove the "neg" constructors from the "pos" ones. + // Remember, VarLenSlice(n) covers the union of FixedLenSlice from + // n to infinity. + for neg_ctor in used_ctors { + remaining_ctors = remaining_ctors + .into_iter() + .flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> { + // Compute pos_ctor \ neg_ctor + match (&pos_ctor, neg_ctor) { + (FixedLenSlice(pos_len), VarLenSlice(neg_len)) => { + if neg_len <= pos_len { + smallvec![] + } else { + smallvec![pos_ctor] + } + } + _ if &pos_ctor == neg_ctor => smallvec![], + _ => smallvec![pos_ctor], + } + }) + .collect(); + + // If the constructors that have been considered so far already cover + // the entire range of `self`, no need to look at more constructors. + if remaining_ctors.is_empty() { + break; + } + } + + remaining_ctors + } ConstantRange(..) | ConstantValue(..) => { let mut remaining_ctors = smallvec![self]; @@ -894,6 +942,7 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, + VarLenSlice(_) => bug!("Trying to apply the variable-length slice constructor"), ConstantValue(_) | MissingConstructors(_) | ConstantRange(..) | Wildcard => vec![], }; @@ -917,7 +966,7 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), _ => 0, }, - FixedLenSlice(length) => *length, + FixedLenSlice(length) | VarLenSlice(length) => *length, ConstantValue(_) | ConstantRange(..) | Wildcard | MissingConstructors(_) => 0, } } @@ -969,6 +1018,7 @@ impl<'tcx> Constructor<'tcx> { FixedLenSlice(_) => { PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } } + VarLenSlice(_) => bug!("Trying to apply the variable-length slice constructor"), 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)), @@ -1157,7 +1207,7 @@ fn all_constructors<'a, 'tcx>( if cx.is_uninhabited(sub_ty) { vec![FixedLenSlice(0)] } else { - (0..pcx.max_slice_length + 1).map(|length| FixedLenSlice(length)).collect() + vec![VarLenSlice(0)] } } ty::Adt(def, substs) if def.is_enum() => def @@ -1495,6 +1545,7 @@ impl<'tcx> IntRange<'tcx> { // A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. #[derive(Clone)] struct MissingConstructors<'tcx> { + pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, @@ -1509,12 +1560,13 @@ type MissingConstructorsIter<'a, 'tcx, F> = std::iter::FlatMap< impl<'tcx> MissingConstructors<'tcx> { fn new( + pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, ) -> Self { - MissingConstructors { tcx, param_env, all_ctors, used_ctors } + MissingConstructors { pcx, tcx, param_env, all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { @@ -1535,7 +1587,12 @@ impl<'tcx> MissingConstructors<'tcx> { impl FnMut(&'a Constructor<'tcx>) -> SmallVec<[Constructor<'tcx>; 1]>, > { self.all_ctors.iter().flat_map(move |req_ctor| { - req_ctor.clone().subtract_meta_constructor(self.tcx, self.param_env, &self.used_ctors) + req_ctor.clone().subtract_meta_constructor( + self.pcx, + self.tcx, + self.param_env, + &self.used_ctors, + ) }) } } @@ -1654,8 +1711,8 @@ pub fn is_useful<'p, 'a, 'tcx>( .unwrap_or(NotUseful) } -/// A shorthand for the `U(S(c, P), S(c, q))` operation from the paper. I.e., `is_useful` applied -/// to the specialised version of both the pattern matrix `P` and the new pattern `q`. +/// A shorthand for the `U(S(c, M), S(c, q))` operation. I.e., `is_useful` applied +/// to the specialised version of both the pattern matrix `M` and the new pattern `q`. fn is_useful_specialized<'p, 'a, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, matrix: &Matrix<'p, 'tcx>, @@ -1712,7 +1769,7 @@ fn pat_constructors<'tcx>( PatKind::Slice { ref prefix, ref slice, ref suffix } => { let pat_len = prefix.len() as u64 + suffix.len() as u64; if slice.is_some() { - (pat_len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() + smallvec![VarLenSlice(pat_len)] } else { smallvec![FixedLenSlice(pat_len)] } From 1793ad56ac0aafa85b778e69536b77cee91718cb Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 2 Oct 2019 17:42:29 +0200 Subject: [PATCH 47/87] Don't use max_slice_length when subtracting from VarLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 66 +++++++++++++++++++------ 1 file changed, 51 insertions(+), 15 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 0892e0b4dab3b..1988dde125a2f 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -797,7 +797,7 @@ impl<'tcx> Constructor<'tcx> { /// by the constructors covered by `head_ctors`: i.e., `self \ head_ctors` (in set notation). fn subtract_meta_constructor( self, - pcx: PatCtxt<'tcx>, + _pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, used_ctors: &Vec>, @@ -816,11 +816,7 @@ impl<'tcx> Constructor<'tcx> { } } FixedLenSlice(_) | VarLenSlice(_) => { - let mut remaining_ctors = if let VarLenSlice(len) = &self { - (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect() - } else { - smallvec![self] - }; + let mut remaining_ctors = smallvec![self]; // For each used ctor, subtract from the current set of constructors. // Naming: we remove the "neg" constructors from the "pos" ones. @@ -839,6 +835,23 @@ impl<'tcx> Constructor<'tcx> { smallvec![pos_ctor] } } + (VarLenSlice(pos_len), VarLenSlice(neg_len)) => { + if neg_len <= pos_len { + smallvec![] + } else { + (*pos_len..*neg_len).map(FixedLenSlice).collect() + } + } + (VarLenSlice(pos_len), FixedLenSlice(neg_len)) => { + if neg_len < pos_len { + smallvec![pos_ctor] + } else { + (*pos_len..*neg_len) + .map(FixedLenSlice) + .chain(Some(VarLenSlice(neg_len + 1))) + .collect() + } + } _ if &pos_ctor == neg_ctor => smallvec![], _ => smallvec![pos_ctor], } @@ -938,11 +951,10 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(ty) | ty::Array(ty, _) => bug!("bad slice pattern {:?} {:?}", self, ty), _ => vec![], }, - FixedLenSlice(length) => match ty.kind { + FixedLenSlice(length) | VarLenSlice(length) => match ty.kind { ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, - VarLenSlice(_) => bug!("Trying to apply the variable-length slice constructor"), ConstantValue(_) | MissingConstructors(_) | ConstantRange(..) | Wildcard => vec![], }; @@ -985,6 +997,7 @@ impl<'tcx> Constructor<'tcx> { fn apply<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, + pcx: PatCtxt<'tcx>, ty: Ty<'tcx>, pats: impl IntoIterator>, ) -> SmallVec<[Pat<'tcx>; 1]> { @@ -1018,7 +1031,26 @@ impl<'tcx> Constructor<'tcx> { FixedLenSlice(_) => { PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } } - VarLenSlice(_) => bug!("Trying to apply the variable-length slice constructor"), + VarLenSlice(_) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => { + let prefix: Vec<_> = pats.collect(); + let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; + // To keep identical diagnostics, we list all possible lengths until max_slice_length. + return (prefix.len()..=pcx.max_slice_length as usize) + .map(|len| { + let prefix = prefix + .iter() + .cloned() + .chain(std::iter::repeat(wild.clone()).take(len - prefix.len())) + .collect(); + let pat = + PatKind::Slice { prefix: prefix, slice: None, suffix: vec![] }; + Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } + }) + .collect(); + } + _ => bug!("bad slice pattern {:?} {:?}", self, ty), + }, 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)), @@ -1033,7 +1065,7 @@ impl<'tcx> Constructor<'tcx> { // `Option::Some`, we get the pattern `Some(_)`. return missing_ctors .iter() - .flat_map(|ctor| ctor.apply_wildcards(cx, ty)) + .flat_map(|ctor| ctor.apply_wildcards(cx, pcx, ty)) .collect(); } }; @@ -1045,10 +1077,11 @@ impl<'tcx> Constructor<'tcx> { fn apply_wildcards<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, + pcx: PatCtxt<'tcx>, ty: Ty<'tcx>, ) -> SmallVec<[Pat<'tcx>; 1]> { let pats = self.wildcard_subpatterns(cx, ty).rev(); - self.apply(cx, ty, pats) + self.apply(cx, pcx, ty, pats) } } @@ -1077,6 +1110,7 @@ impl<'tcx> Usefulness<'tcx> { fn apply_constructor( self, cx: &MatchCheckCtxt<'_, 'tcx>, + pcx: PatCtxt<'tcx>, ctor: &Constructor<'tcx>, lty: Ty<'tcx>, ) -> Self { @@ -1084,7 +1118,7 @@ impl<'tcx> Usefulness<'tcx> { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses .into_iter() - .flat_map(|witness| witness.apply_constructor(cx, &ctor, lty)) + .flat_map(|witness| witness.apply_constructor(cx, pcx, &ctor, lty)) .collect(), ), x => x, @@ -1161,6 +1195,7 @@ impl<'tcx> Witness<'tcx> { fn apply_constructor<'a>( mut self, cx: &MatchCheckCtxt<'a, 'tcx>, + pcx: PatCtxt<'tcx>, ctor: &Constructor<'tcx>, ty: Ty<'tcx>, ) -> SmallVec<[Self; 1]> { @@ -1168,7 +1203,7 @@ impl<'tcx> Witness<'tcx> { let applied_pats = { let len = self.0.len() as u64; let pats = self.0.drain((len - arity) as usize..).rev(); - ctor.apply(cx, ty, pats) + ctor.apply(cx, pcx, ty, pats) }; applied_pats @@ -1706,7 +1741,7 @@ pub fn is_useful<'p, 'a, 'tcx>( v_constructors .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &matrix_head_ctors)) - .map(|c| is_useful_specialized(cx, matrix, v, c, pcx.ty, witness_preference)) + .map(|c| is_useful_specialized(cx, pcx, matrix, v, c, pcx.ty, witness_preference)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } @@ -1715,6 +1750,7 @@ pub fn is_useful<'p, 'a, 'tcx>( /// to the specialised version of both the pattern matrix `M` and the new pattern `q`. fn is_useful_specialized<'p, 'a, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, + pcx: PatCtxt<'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, @@ -1728,7 +1764,7 @@ fn is_useful_specialized<'p, 'a, 'tcx>( let matrix = matrix.specialize(cx, &ctor, &ctor_wild_subpatterns); v.specialize(cx, &ctor, &ctor_wild_subpatterns) .map(|v| is_useful(cx, &matrix, &v, witness_preference)) - .map(|u| u.apply_constructor(cx, &ctor, lty)) + .map(|u| u.apply_constructor(cx, pcx, &ctor, lty)) .unwrap_or(NotUseful) } From 527fe65bd9aef1b55ad5a5025cbcacdedfffe181 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 2 Oct 2019 17:50:37 +0200 Subject: [PATCH 48/87] Faster code path for subtracting from FixedLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 1988dde125a2f..acbb1d815bed7 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -815,7 +815,15 @@ impl<'tcx> Constructor<'tcx> { smallvec![self] } } - FixedLenSlice(_) | VarLenSlice(_) => { + FixedLenSlice(self_len) => { + let overlaps = |c: &Constructor<'_>| match c { + FixedLenSlice(other_len) => other_len == self_len, + VarLenSlice(other_len) => other_len <= self_len, + _ => false, + }; + if used_ctors.iter().any(overlaps) { smallvec![] } else { smallvec![self] } + } + VarLenSlice(_) => { let mut remaining_ctors = smallvec![self]; // For each used ctor, subtract from the current set of constructors. From 0d58ead4f4d958e9844425b7158c363729ad57c5 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 11:08:57 +0100 Subject: [PATCH 49/87] Faster code path for subtracting from VarLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 118 ++++++++++++++---------- 1 file changed, 70 insertions(+), 48 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index acbb1d815bed7..d42b9ec866242 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -805,7 +805,7 @@ impl<'tcx> Constructor<'tcx> { debug!("subtract_meta_constructor {:?}", self); assert!(!used_ctors.iter().any(|c| c.is_wildcard())); - match &self { + match self { // Those constructors can't match a non-wildcard metaconstructor, so we're fine // just comparing for equality. Single | Variant(_) => { @@ -817,62 +817,84 @@ impl<'tcx> Constructor<'tcx> { } FixedLenSlice(self_len) => { let overlaps = |c: &Constructor<'_>| match c { - FixedLenSlice(other_len) => other_len == self_len, - VarLenSlice(other_len) => other_len <= self_len, + FixedLenSlice(other_len) => *other_len == self_len, + VarLenSlice(other_len) => *other_len <= self_len, _ => false, }; if used_ctors.iter().any(overlaps) { smallvec![] } else { smallvec![self] } } - VarLenSlice(_) => { - let mut remaining_ctors = smallvec![self]; + VarLenSlice(self_len) => { + // Initially we cover all slice lengths starting from self_len. - // For each used ctor, subtract from the current set of constructors. - // Naming: we remove the "neg" constructors from the "pos" ones. - // Remember, VarLenSlice(n) covers the union of FixedLenSlice from - // n to infinity. - for neg_ctor in used_ctors { - remaining_ctors = remaining_ctors - .into_iter() - .flat_map(|pos_ctor| -> SmallVec<[Constructor<'tcx>; 1]> { - // Compute pos_ctor \ neg_ctor - match (&pos_ctor, neg_ctor) { - (FixedLenSlice(pos_len), VarLenSlice(neg_len)) => { - if neg_len <= pos_len { - smallvec![] - } else { - smallvec![pos_ctor] - } - } - (VarLenSlice(pos_len), VarLenSlice(neg_len)) => { - if neg_len <= pos_len { - smallvec![] - } else { - (*pos_len..*neg_len).map(FixedLenSlice).collect() - } - } - (VarLenSlice(pos_len), FixedLenSlice(neg_len)) => { - if neg_len < pos_len { - smallvec![pos_ctor] - } else { - (*pos_len..*neg_len) - .map(FixedLenSlice) - .chain(Some(VarLenSlice(neg_len + 1))) - .collect() - } - } - _ if &pos_ctor == neg_ctor => smallvec![], - _ => smallvec![pos_ctor], - } - }) - .collect(); + // If there is a VarLenSlice(n) in used_ctors, then we have to discard + // all lengths >= n. So we pick the smallest one. + let max_len: Option<_> = used_ctors + .iter() + .filter_map(|c: &Constructor<'tcx>| match c { + VarLenSlice(other_len) => Some(*other_len), + _ => None, + }) + .min(); - // If the constructors that have been considered so far already cover - // the entire range of `self`, no need to look at more constructors. - if remaining_ctors.is_empty() { - break; + // If max_len <= self_len there are no lengths remaining. + if let Some(max_len) = max_len { + if max_len <= self_len { + return smallvec![]; } } + // The remaining range of lengths is now either `self_len..` + // or `self_len..max_len`. We then remove from that range all the + // individual FixedLenSlice lengths in used_ctors. For that, + // we extract all those lengths that are in our remaining range and + // sort them. Every such length becomes a boundary between ranges + // of lengths that will remain. + #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + enum Length { + Start, + Boundary(u64), + } + use Length::*; + + let mut lengths: Vec<_> = used_ctors + .iter() + .filter_map(|c: &Constructor<'tcx>| match c { + FixedLenSlice(other_len) => Some(*other_len), + _ => None, + }) + .filter(|l| *l >= self_len) + .filter(|l| match max_len { + Some(max_len) => *l < max_len, + None => true, + }) + .chain(max_len) // Add max_len as the final boundary + .map(Boundary) + .chain(Some(Start)) // Add a special starting boundary + .collect(); + lengths.sort_unstable(); + lengths.dedup(); + + let mut remaining_ctors: SmallVec<_> = lengths + .windows(2) + .flat_map(|window| match (window[0], window[1]) { + (Boundary(n), Boundary(m)) => (n + 1..m), + (Start, Boundary(m)) => (self_len..m), + _ => bug!(), + }) + .map(FixedLenSlice) + .collect(); + + // If there was a max_len, then we're done. Otherwise, we + // still need to include all lengths starting from the longest + // one til infinity, using VarLenSlice. + if max_len.is_none() { + let final_length = match lengths.last().unwrap() { + Start => self_len, + Boundary(n) => n + 1, + }; + remaining_ctors.push(VarLenSlice(final_length)); + } + remaining_ctors } ConstantRange(..) | ConstantValue(..) => { From 0b353b34b3059a765ca17f398890048fc0ba69c9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 14:21:03 +0100 Subject: [PATCH 50/87] Make exhaustiveness error message more consistent for slice patterns Now it doesn't depend on the rather arbitrary (from the user's point of view) max_slice_length limit. When slice_patterns is not enabled, and the missing pattern is e.g. `[_, _, ..]`, the message will report that `[_, _]` is missing. Previously, it would sometimes report a few more missing patterns but of course never all of them. When the feature is enabled, the message correctly reports that `[_, _, ..]` is missing. --- src/librustc_mir/hair/pattern/_match.rs | 29 +++++++++---------- .../match-byte-array-patterns-2.stderr | 4 +-- .../usefulness/non-exhaustive-match.rs | 2 +- .../usefulness/non-exhaustive-match.stderr | 4 +-- .../ui/pattern/usefulness/slice-patterns.rs | 4 +-- .../pattern/usefulness/slice-patterns.stderr | 8 ++--- 6 files changed, 24 insertions(+), 27 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index d42b9ec866242..cc82c91c49eda 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1063,21 +1063,18 @@ impl<'tcx> Constructor<'tcx> { } VarLenSlice(_) => match ty.kind { ty::Slice(ty) | ty::Array(ty, _) => { - let prefix: Vec<_> = pats.collect(); - let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; - // To keep identical diagnostics, we list all possible lengths until max_slice_length. - return (prefix.len()..=pcx.max_slice_length as usize) - .map(|len| { - let prefix = prefix - .iter() - .cloned() - .chain(std::iter::repeat(wild.clone()).take(len - prefix.len())) - .collect(); - let pat = - PatKind::Slice { prefix: prefix, slice: None, suffix: vec![] }; - Pat { ty, span: DUMMY_SP, kind: Box::new(pat) } - }) - .collect(); + let prefix = pats.collect(); + if cx.tcx.features().slice_patterns { + let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; + PatKind::Slice { prefix, slice: Some(wild), suffix: vec![] } + } else { + // We don't want to output a variable-length slice pattern if the slice_patterns + // feature is not enabled. + // The constructor covers infinitely many slice lengths, but for diagnostic purposes + // it is correct to return only some examples of non-covered patterns. So we just + // return the smallest length pattern here. + PatKind::Slice { prefix, slice: None, suffix: vec![] } + } } _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, @@ -1089,7 +1086,7 @@ impl<'tcx> Constructor<'tcx> { }), Wildcard => PatKind::Wild, MissingConstructors(missing_ctors) => { - // Construct for each missing constructor a "wild" version of this + // Construct for each missing constructor a "wildcard" 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(_)`. diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index d53e2e25b3dbd..9938c9c284d1c 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -6,11 +6,11 @@ LL | match buf { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]`, `&[_, _]` and 3 more not covered +error[E0004]: non-exhaustive patterns: `&[]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ patterns `&[]`, `&[_]`, `&[_, _]` and 3 more not covered + | ^^^ pattern `&[]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs index 0e5a9203c5f80..bfca5352353a7 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.rs +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.rs @@ -44,7 +44,7 @@ fn main() { } let vec = vec![0.5f32]; let vec: &[f32] = &vec; - match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _]` not covered + match *vec { //~ ERROR non-exhaustive patterns: `[_, _, _, _, ..]` not covered [0.1, 0.2, 0.3] => (), [0.1, 0.2] => (), [0.1] => (), diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr index 5dba05e16427a..577867e4e7122 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-match.stderr @@ -66,11 +66,11 @@ LL | match *vec { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `[_, _, _, _]` not covered +error[E0004]: non-exhaustive patterns: `[_, _, _, _, ..]` not covered --> $DIR/non-exhaustive-match.rs:47:11 | LL | match *vec { - | ^^^^ pattern `[_, _, _, _]` not covered + | ^^^^ pattern `[_, _, _, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs index 0493646ff3bba..97086c4d75ddc 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -48,11 +48,11 @@ fn main() { [true, .., true] => {} } match s { - //~^ ERROR `&[_]` not covered + //~^ ERROR `&[_, ..]` not covered [] => {} } match s { - //~^ ERROR `&[_, _]` not covered + //~^ ERROR `&[_, _, ..]` not covered [] => {} [_] => {} } diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr index 18c73330fcdc1..3cea068543ebd 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -30,19 +30,19 @@ LL | match s3 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_]` not covered +error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered --> $DIR/slice-patterns.rs:50:11 | LL | match s { - | ^ pattern `&[_]` not covered + | ^ pattern `&[_, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_, _]` not covered +error[E0004]: non-exhaustive patterns: `&[_, _, ..]` not covered --> $DIR/slice-patterns.rs:54:11 | LL | match s { - | ^ pattern `&[_, _]` not covered + | ^ pattern `&[_, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From cf4dc88623ab44faf5bdb0035081bc57b5ba246f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 16:15:55 +0100 Subject: [PATCH 51/87] Prepare return types for or-patterns --- src/librustc_mir/hair/pattern/_match.rs | 75 +++++++++++++++---------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index cc82c91c49eda..491769f4010e5 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -348,16 +348,19 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { cx: &MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], - ) -> Option> + ) -> SmallVec<[PatStack<'p2, 'tcx>; 1]> where 'a: 'p2, 'p: 'p2, { let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); - let result = new_heads.map(|mut new_head| { - new_head.0.extend_from_slice(&self.0[1..]); - new_head - }); + let result = new_heads + .into_iter() + .map(|mut new_head| { + new_head.0.extend_from_slice(&self.0[1..]); + new_head + }) + .collect(); debug!("specialize({:#?}, {:#?}) = {:#?}", self, constructor, result); result } @@ -1789,10 +1792,14 @@ 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(cx, &ctor, &ctor_wild_subpatterns); - v.specialize(cx, &ctor, &ctor_wild_subpatterns) + let ret = v + .specialize(cx, &ctor, &ctor_wild_subpatterns) + .into_iter() .map(|v| is_useful(cx, &matrix, &v, witness_preference)) .map(|u| u.apply_constructor(cx, pcx, &ctor, lty)) - .unwrap_or(NotUseful) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful); + ret } /// Determines the constructors that the given pattern can be specialized to. @@ -2011,7 +2018,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( mut pat: &'p2 Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], -) -> Option> { +) -> SmallVec<[PatStack<'p, 'tcx>; 1]> { while let PatKind::AscribeUserType { ref subpattern, .. } = *pat.kind { pat = subpattern; } @@ -2020,8 +2027,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( // By construction of MissingConstructors, we know that all non-wildcard constructors // should be discarded. return match *pat.kind { - PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), - _ => None, + PatKind::Binding { .. } | PatKind::Wild => smallvec![PatStack::empty()], + _ => smallvec![], }; } else if let Wildcard = constructor { // If we get here, either there were only wildcards in the first component of the @@ -2029,8 +2036,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( // an extra `_` constructor to prevent exhaustive matching. In both cases, all // non-wildcard constructors should be discarded. return match *pat.kind { - PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), - _ => None, + PatKind::Binding { .. } | PatKind::Wild => smallvec![PatStack::empty()], + _ => smallvec![], }; } @@ -2038,21 +2045,23 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( PatKind::AscribeUserType { .. } => unreachable!(), // Handled above PatKind::Binding { .. } | PatKind::Wild => { - Some(PatStack::from_slice(ctor_wild_subpatterns)) + smallvec![PatStack::from_slice(ctor_wild_subpatterns)] } PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; - Some(Variant(variant.def_id)) - .filter(|variant_constructor| variant_constructor == constructor) - .map(|_| patterns_for_variant(subpatterns, ctor_wild_subpatterns)) + if Variant(variant.def_id) == *constructor { + smallvec![patterns_for_variant(subpatterns, ctor_wild_subpatterns)] + } else { + smallvec![] + } } PatKind::Leaf { ref subpatterns } => { - Some(patterns_for_variant(subpatterns, ctor_wild_subpatterns)) + smallvec![patterns_for_variant(subpatterns, ctor_wild_subpatterns)] } - PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(subpattern)), + PatKind::Deref { ref subpattern } => smallvec![PatStack::from_pattern(subpattern)], PatKind::Constant { value } if constructor.is_slice() => { // We extract an `Option` for the pointer because slices of zero @@ -2073,7 +2082,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( } ConstValue::ByRef { .. } => { // FIXME(oli-obk): implement `deref` for `ConstValue` - return None; + return smallvec![]; } _ => span_bug!( pat.span, @@ -2091,9 +2100,13 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( }; 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 layout = if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) { + layout + } else { + return smallvec![]; + }; let ptr = Pointer::new(AllocId(0), offset); - (0..n) + let stack: Option> = (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()?; @@ -2103,9 +2116,10 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( Pat { ty, span: pat.span, kind: box PatKind::Constant { value } }; Some(&*cx.pattern_arena.alloc(pattern)) }) - .collect() + .collect(); + stack.into_iter().collect() } else { - None + smallvec![] } } @@ -2114,6 +2128,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( // - Single value: add a row if the pattern contains the constructor. // - Range: add a row if the constructor intersects the pattern. constructor_intersects_pattern(cx.tcx, cx.param_env, constructor, pat) + .into_iter() + .collect() } PatKind::Array { ref prefix, ref slice, ref suffix } @@ -2122,7 +2138,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { - Some( + smallvec![ prefix .iter() .chain( @@ -2134,12 +2150,12 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( .chain(suffix.iter()), ) .collect(), - ) + ] } else { - None + smallvec![] } } else { - None + smallvec![] } } ConstantValue(cv) => { @@ -2152,9 +2168,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( suffix, cx.param_env, ) { - Ok(true) => Some(PatStack::default()), - Ok(false) => None, - Err(ErrorReported) => None, + Ok(true) => smallvec![PatStack::default()], + Ok(false) | Err(ErrorReported) => smallvec![], } } _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), From 1e43487b31205091e8cdd590ff73ed6831d9bd3e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 20:06:14 +0100 Subject: [PATCH 52/87] MissingConstructors should not be compared for equality, but we need the impl --- src/librustc_mir/hair/pattern/_match.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 491769f4010e5..8929f838e521f 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1669,12 +1669,10 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { } } -// TODO: we should never need that and it's very expensive +/// The implementation panics because this should not happen impl<'tcx> PartialEq for MissingConstructors<'tcx> { - fn eq(&self, other: &Self) -> bool { - let self_ctors: Vec<_> = self.iter().collect(); - let other_ctors: Vec<_> = other.iter().collect(); - self_ctors == other_ctors + fn eq(&self, _other: &Self) -> bool { + bug!("Tried to compare MissingConstructors for equality") } } From c8ac41e81ae821ec2ac6d6ecb7ebe7f4fce8854f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 20:25:48 +0100 Subject: [PATCH 53/87] Add example run of the algorithm --- src/librustc_mir/hair/pattern/_match.rs | 56 +++++++++++++++++-------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8929f838e521f..fd2ea80f137f0 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -109,30 +109,50 @@ /// Note that for c ϵ pat_constructors(p_1), `S(c, P)` always returns exactly one element, so the /// formula above makes sense. /// -/// TODO: example run of the algorithm +/// This algorithm however has a lot of practical issues. Most importantly, it may not terminate +/// in the presence of recursive types, since we always unpack all constructors as much +/// as possible. And it would be stupidly slow anyways for types with a lot of constructors, +/// like `u64` of `&[bool]`. We therefore present a modified version after the example. +/// +/// +/// # Example run of the algorithm +/// +/// Assume we have the following match. We want to know whether it is exhaustive, i.e. whether +/// an additional `_` pattern would be useful (would be reachable). /// ``` -/// // x: (Option, Result<()>) /// match x { -/// (Some(true), _) => {} -/// (None, Err(())) => {} -/// (None, Err(_)) => {} +/// Some(true) => {} +/// None => {} /// } /// ``` -/// Here, the matrix `M` starts as: -/// [ -/// [(Some(true), _)], -/// [(None, Err(()))], -/// [(None, Err(_))], -/// ] -/// We can tell it's not exhaustive, because `U(M, _)` is true (we're not covering -/// `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because -/// all the values it covers are already covered by row 2. /// +/// We start with the following `M` and `p`: +/// M = [ [Some(true)], +/// [None] ] +/// p = [_] +/// `pat_constructors(p)` returns `[None, Some]` /// -/// This algorithm however has a lot of practical issues. Most importantly, it may not terminate -/// in the presence of recursive types, since we always unpack all constructors as much -/// as possible. And it would be stupidly slow anyways for types with a lot of constructors, -/// like `u64` of `&[bool]`. +/// We specialize on the `None` constructor first: +/// S(None, M) = [ [] ] +/// S(None, p) = [] +/// We hit the base case n = 0: since bool is inhabited, `U(S(None, M), S(None, p)) = false`. +/// +/// We specialize on the `Some` constructor second: +/// S(Some, M) = [ [true] ] +/// S(Some, p) = [_] +/// Let M' := S(Some, M) and p' := S(Some, p). +/// +/// `pat_constructors(p')` returns `[true, false]` +/// S(true, M') = [ [] ] +/// S(true, p') = [] +/// So `U(S(true, M'), S(true, p')) = false` +/// +/// S(false, M') = [] +/// S(false, p') = [] +/// So `U(S(false, M'), S(false, p')) = true` +/// +/// Therefore `U(M, p) = true`, indeed by following the steps taken we can recover that +/// the pattern `Some(false)` was not covered by the initial match. /// /// /// # Concrete algorithm From 244ac8dd748da552710b42b0fb7a3b1d9ee18275 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 20:35:57 +0100 Subject: [PATCH 54/87] Tidy up --- src/librustc_mir/hair/pattern/_match.rs | 121 +++++++++++++----------- 1 file changed, 66 insertions(+), 55 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index fd2ea80f137f0..afb0fbb8abd4e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -51,11 +51,12 @@ /// or `None`). This operation returns zero or more altered pattern-stacks, as follows. /// We look at the pattern `p_1` on top of the stack, and we have four cases: /// 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: +/// onto the stack the arguments of this constructor, and return the result: /// r_1, .., r_a, p_2, .., p_n -/// 2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return nothing. +/// 2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return +/// nothing. /// 3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` -/// has arguments (its arity), and return the resulting stack: +/// has arguments (its arity), and return the resulting stack: /// _, .., _, p_2, .., p_n /// 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)) @@ -84,15 +85,18 @@ /// Inductive step. (`n > 0`) /// We look at `p_1`, the head of the pattern-stack `p`. /// -/// We first generate the list of constructors that are covered by a pattern `pat`. We name this operation -/// `pat_constructors`. -/// - If `pat == c(r_1, .., r_a)`, i.e. we have a constructor pattern. Then we just return `c`: +/// We first generate the list of constructors that are covered by a pattern `pat`. We name +/// this operation `pat_constructors`. +/// - If `pat == c(r_1, .., r_a)`, i.e. we have a constructor pattern. Then we just +/// return `c`: /// `pat_constructors(pat) = [c]` /// -/// - If `pat == _`, then we return the list of all possible constructors for the relevant type: +/// - If `pat == _`, then we return the list of all possible constructors for the +/// relevant type: /// `pat_constructors(pat) = all_constructors(pat.ty)` /// -/// - If `pat == r_1 | r_2`, then we return the constructors for either branch of the OR-pattern: +/// - If `pat == r_1 | r_2`, then we return the constructors for either branch of the +/// OR-pattern: /// `pat_constructors(pat) = pat_constructors(r_1) + pat_constructors(r_2)` /// /// Then for each constructor `c` in `pat_constructors(p_1)`, we want to check whether a value @@ -101,13 +105,13 @@ /// For that, we only care about those rows of `M` whose first component covers the /// constructor `c`; and for those rows that do, we want to unpack the arguments to `c` to check /// further that `p` matches additional values. -/// This is where specialization comes in: this check amounts to computing `U(S(c, M), S(c, p))`. -/// More details can be found in the paper. +/// This is where specialization comes in: this check amounts to computing `U(S(c, M), S(c, +/// p))`. More details can be found in the paper. /// /// Thus we get: `U(M, p) := ∃(c ϵ pat_constructors(p_1)) U(S(c, M), S(c, p))` /// -/// Note that for c ϵ pat_constructors(p_1), `S(c, P)` always returns exactly one element, so the -/// formula above makes sense. +/// Note that for c ϵ pat_constructors(p_1), `S(c, P)` always returns exactly one element, so +/// the formula above makes sense. /// /// This algorithm however has a lot of practical issues. Most importantly, it may not terminate /// in the presence of recursive types, since we always unpack all constructors as much @@ -601,7 +605,7 @@ impl<'tcx> Constructor<'tcx> { match self { Wildcard => true, MissingConstructors(_) => bug!( - "Not sure if MissingConstructors should count as a wildcard. Shouldn't happen anyways." + "Not sure if MissingConstructors should be a wildcard. Shouldn't happen anyways." ), _ => false, } @@ -640,38 +644,44 @@ impl<'tcx> Constructor<'tcx> { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], ConstantRange(..) if should_treat_range_exhaustively(cx.tcx, &self) => { - // For exhaustive integer matching, some constructors are grouped within other constructors - // (namely integer typed values are grouped within ranges). However, when specialising these - // constructors, we want to be specialising for the underlying constructors (the integers), not - // the groups (the ranges). Thus we need to split the groups up. Splitting them up naïvely would - // mean creating a separate constructor for every single value in the range, which is clearly - // impractical. However, observe that for some ranges of integers, the specialisation will be - // identical across all values in that range (i.e., there are equivalence classes of ranges of - // constructors based on their `is_useful_specialized` outcome). These classes are grouped by - // the patterns that apply to them (in the matrix `P`). We can split the range whenever the - // patterns that apply to that range (specifically: the patterns that *intersect* with that range) - // change. - // Our solution, therefore, is to split the range constructor into subranges at every single point - // the group of intersecting patterns changes (using the method described below). - // And voilà! We're testing precisely those ranges that we need to, without any exhaustive matching - // on actual integers. The nice thing about this is that the number of subranges is linear in the - // number of rows in the matrix (i.e., the number of cases in the `match` statement), so we don't - // need to be worried about matching over gargantuan ranges. + // For exhaustive integer matching, some constructors are grouped within other + // constructors (namely integer typed values are grouped within ranges). However, + // when specialising these constructors, we want to be specialising for the + // underlying constructors (the integers), not the groups (the ranges). Thus we + // need to split the groups up. Splitting them up naïvely would mean creating a + // separate constructor for every single value in the range, which is clearly + // impractical. However, observe that for some ranges of integers, the + // specialisation will be identical across all values in that range (i.e., there + // are equivalence classes of ranges of constructors based on their + // `is_useful_specialized` outcome). These classes are grouped by the patterns that + // apply to them (in the matrix `P`). We can split the range whenever the patterns + // that apply to that range (specifically: the patterns that *intersect* with that + // range) change. + // Our solution, therefore, is to split the range constructor into subranges at + // every single point the group of intersecting patterns changes (using the method + // described below). And voilà! We're testing precisely those ranges that we need + // to, without any exhaustive matching on actual integers. The nice thing about + // this is that the number of subranges is linear in the number of rows in the + // matrix (i.e., the number of cases in the `match` statement), so we don't need to + // be worried about matching over gargantuan ranges. // - // Essentially, given the first column of a matrix representing ranges, looking like the following: + // Essentially, given the first column of a matrix representing ranges, looking + // like the following: // // |------| |----------| |-------| || // |-------| |-------| |----| || // |---------| // - // We split the ranges up into equivalence classes so the ranges are no longer overlapping: + // We split the ranges up into equivalence classes so the ranges are no longer + // overlapping: // // |--|--|||-||||--||---|||-------| |-|||| || // - // The logic for determining how to split the ranges is fairly straightforward: we calculate - // boundaries for each interval range, sort them, then create constructors for each new interval - // between every pair of boundary points. (This essentially sums up to performing the intuitive - // merging operation depicted above.) + // The logic for determining how to split the ranges is fairly straightforward: we + // calculate boundaries for each interval range, sort them, then create + // constructors for each new interval between every pair of boundary points. (This + // essentially sums up to performing the intuitive merging operation depicted + // above.) // We only care about finding all the subranges within the range of the constructor // range. Anything else is irrelevant, because it is guaranteed to result in @@ -709,9 +719,9 @@ impl<'tcx> Constructor<'tcx> { let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); borders.sort_unstable(); - // 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. + // 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. borders .windows(2) .filter_map(|window| match (window[0], window[1]) { @@ -785,8 +795,8 @@ impl<'tcx> Constructor<'tcx> { // to them and we can ignore the other ones. Otherwise, we have to try all // existing constructors one-by-one. if is_non_exhaustive { - // We pretend the type has an additional `_` constructor, that counts as a missing - // constructor. So we return that constructor. + // We pretend the type has an additional `_` constructor, that counts as a + // missing constructor. So we return that constructor. smallvec![Wildcard] } else if !missing_ctors.is_empty() { if head_ctors.is_empty() { @@ -795,17 +805,17 @@ impl<'tcx> Constructor<'tcx> { smallvec![Wildcard] } else { // Otherwise, we have a set of missing constructors that is neither empty - // not equal to all_constructors. Since all missing constructors will behave - // the same (i.e. will be matched only by wildcards), we return a metaconstructor - // that contains all of them at once. + // not equal to all_constructors. Since all missing constructors will + // behave the same (i.e. will be matched only by wildcards), we return a + // metaconstructor that contains all of them at once. smallvec![MissingConstructors(missing_ctors)] } } else { - // Here we know there are no missing constructors, so we have to try all existing - // constructors one-by-one. + // Here we know there are no missing constructors, so we have to try all + // existing constructors one-by-one. let (all_ctors, _) = missing_ctors.into_inner(); - // Recursively split newly generated list of constructors. This list must not contain - // any wildcards so we don't recurse infinitely. + // Recursively split newly generated list of constructors. This list must not + // contain any wildcards so we don't recurse infinitely. all_ctors .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, head_ctors)) @@ -816,8 +826,9 @@ impl<'tcx> Constructor<'tcx> { } } - /// Returns a collection of constructors that spans the constructors covered by `self`, subtracted - /// by the constructors covered by `head_ctors`: i.e., `self \ head_ctors` (in set notation). + /// Returns a collection of constructors that spans the constructors covered by `self`, + /// subtracted by the constructors covered by `head_ctors`: i.e., `self \ head_ctors` (in set + /// notation). fn subtract_meta_constructor( self, _pcx: PatCtxt<'tcx>, @@ -1091,11 +1102,11 @@ impl<'tcx> Constructor<'tcx> { let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; PatKind::Slice { prefix, slice: Some(wild), suffix: vec![] } } else { - // We don't want to output a variable-length slice pattern if the slice_patterns - // feature is not enabled. - // The constructor covers infinitely many slice lengths, but for diagnostic purposes - // it is correct to return only some examples of non-covered patterns. So we just - // return the smallest length pattern here. + // We don't want to output a variable-length slice pattern if the + // slice_patterns feature is not enabled. + // The constructor covers infinitely many slice lengths, but for diagnostic + // purposes it is correct to return only some examples of non-covered + // patterns. So we just return the smallest length pattern here. PatKind::Slice { prefix, slice: None, suffix: vec![] } } } From ad09cc597845eab7033e70659ca340967cbd0d0c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 21:34:04 +0100 Subject: [PATCH 55/87] Store both prefix and suffix length in VarLenSlice --- src/librustc_mir/hair/pattern/_match.rs | 43 +++++++++++++++---------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index afb0fbb8abd4e..543d4a3a13610 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -582,8 +582,8 @@ enum Constructor<'tcx> { // Meta-constructors /// Ranges of literal values (`2..=5` and `2..5`). ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), - /// Slice patterns. Captures any array constructor of length >= n. - VarLenSlice(u64), + /// Slice patterns. Captures any array constructor of length >= i+j. + VarLenSlice(u64, u64), /// Wildcard metaconstructor. Wildcard, /// List of constructors that were _not_ present in the first column @@ -739,7 +739,9 @@ impl<'tcx> Constructor<'tcx> { .collect() } ConstantRange(..) => smallvec![self], - VarLenSlice(len) => (*len..pcx.max_slice_length + 1).map(FixedLenSlice).collect(), + VarLenSlice(prefix, suffix) => { + (prefix + suffix..pcx.max_slice_length + 1).map(FixedLenSlice).collect() + } Wildcard => { let is_declared_nonexhaustive = !cx.is_local(pcx.ty) && cx.is_non_exhaustive_enum(pcx.ty); @@ -852,12 +854,13 @@ impl<'tcx> Constructor<'tcx> { FixedLenSlice(self_len) => { let overlaps = |c: &Constructor<'_>| match c { FixedLenSlice(other_len) => *other_len == self_len, - VarLenSlice(other_len) => *other_len <= self_len, + VarLenSlice(prefix, suffix) => prefix + suffix <= self_len, _ => false, }; if used_ctors.iter().any(overlaps) { smallvec![] } else { smallvec![self] } } - VarLenSlice(self_len) => { + VarLenSlice(self_prefix, self_suffix) => { + let self_len = self_prefix + self_suffix; // Initially we cover all slice lengths starting from self_len. // If there is a VarLenSlice(n) in used_ctors, then we have to discard @@ -865,7 +868,7 @@ impl<'tcx> Constructor<'tcx> { let max_len: Option<_> = used_ctors .iter() .filter_map(|c: &Constructor<'tcx>| match c { - VarLenSlice(other_len) => Some(*other_len), + VarLenSlice(prefix, suffix) => Some(prefix + suffix), _ => None, }) .min(); @@ -926,7 +929,7 @@ impl<'tcx> Constructor<'tcx> { Start => self_len, Boundary(n) => n + 1, }; - remaining_ctors.push(VarLenSlice(final_length)); + remaining_ctors.push(VarLenSlice(final_length, 0)); } remaining_ctors @@ -970,7 +973,7 @@ impl<'tcx> Constructor<'tcx> { ty: Ty<'tcx>, ) -> impl Iterator> + DoubleEndedIterator { debug!("wildcard_subpatterns({:#?}, {:?})", self, ty); - let subpattern_types = match self { + let subpattern_types = match *self { Single | Variant(_) => match ty.kind { ty::Tuple(ref fs) => fs.into_iter().map(|t| t.expect_ty()).collect(), ty::Ref(_, rty, _) => vec![rty], @@ -1015,8 +1018,12 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(ty) | ty::Array(ty, _) => bug!("bad slice pattern {:?} {:?}", self, ty), _ => vec![], }, - FixedLenSlice(length) | VarLenSlice(length) => match ty.kind { - ty::Slice(ty) | ty::Array(ty, _) => (0..*length).map(|_| ty).collect(), + FixedLenSlice(length) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => (0..length).map(|_| ty).collect(), + _ => bug!("bad slice pattern {:?} {:?}", self, ty), + }, + VarLenSlice(prefix, suffix) => match ty.kind { + ty::Slice(ty) | ty::Array(ty, _) => (0..prefix + suffix).map(|_| ty).collect(), _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, ConstantValue(_) | MissingConstructors(_) | ConstantRange(..) | Wildcard => vec![], @@ -1032,7 +1039,7 @@ impl<'tcx> Constructor<'tcx> { /// 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 self { + match *self { Single | Variant(_) => match ty.kind { ty::Tuple(ref fs) => fs.len() as u64, ty::Ref(..) => 1, @@ -1042,7 +1049,8 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(..) | ty::Array(..) => bug!("bad slice pattern {:?} {:?}", self, ty), _ => 0, }, - FixedLenSlice(length) | VarLenSlice(length) => *length, + FixedLenSlice(length) => length, + VarLenSlice(prefix, suffix) => prefix + suffix, ConstantValue(_) | ConstantRange(..) | Wildcard | MissingConstructors(_) => 0, } } @@ -1095,7 +1103,7 @@ impl<'tcx> Constructor<'tcx> { FixedLenSlice(_) => { PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } } - VarLenSlice(_) => match ty.kind { + VarLenSlice(_, _) => match ty.kind { ty::Slice(ty) | ty::Array(ty, _) => { let prefix = pats.collect(); if cx.tcx.features().slice_patterns { @@ -1303,7 +1311,7 @@ fn all_constructors<'a, 'tcx>( if cx.is_uninhabited(sub_ty) { vec![FixedLenSlice(0)] } else { - vec![VarLenSlice(0)] + vec![VarLenSlice(0, 0)] } } ty::Adt(def, substs) if def.is_enum() => def @@ -1866,11 +1874,12 @@ fn pat_constructors<'tcx>( _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { - let pat_len = prefix.len() as u64 + suffix.len() as u64; + let prefix = prefix.len() as u64; + let suffix = suffix.len() as u64; if slice.is_some() { - smallvec![VarLenSlice(pat_len)] + smallvec![VarLenSlice(prefix, suffix)] } else { - smallvec![FixedLenSlice(pat_len)] + smallvec![FixedLenSlice(prefix + suffix)] } } PatKind::Or { .. } => { From 47ee6289f15a36ba38029be0a59a4b4cea573997 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 21:53:33 +0100 Subject: [PATCH 56/87] max_slice_length only needs to look at constructors --- src/librustc_mir/hair/pattern/_match.rs | 108 +++++++++++------------- 1 file changed, 51 insertions(+), 57 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 543d4a3a13610..05c621dcd157d 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1364,38 +1364,33 @@ fn all_constructors<'a, 'tcx>( ctors } -fn max_slice_length<'p, 'a, 'tcx, I>(cx: &MatchCheckCtxt<'a, 'tcx>, patterns: I) -> u64 +fn max_slice_length<'p, 'a, 'tcx, I>(cx: &MatchCheckCtxt<'a, 'tcx>, ctors: I) -> u64 where - I: Iterator>, + I: Iterator>, 'tcx: 'p, { - // The exhaustiveness-checking paper does not include any details on - // checking variable-length slice patterns. However, they are matched - // by an infinite collection of fixed-length array patterns. - // - // Checking the infinite set directly would take an infinite amount - // of time. However, it turns out that for each finite set of - // patterns `P`, all sufficiently large array lengths are equivalent: + // A variable-length slice pattern is matched by an infinite collection of fixed-length array + // patterns. However it turns out that for each finite set of patterns `P`, all sufficiently + // large array lengths are equivalent. // // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies // to exactly the subset `Pₜ` of `P` can be transformed to a slice // `sₘ` for each sufficiently-large length `m` that applies to exactly // the same subset of `P`. // - // Because of that, each witness for reachability-checking from one - // of the sufficiently-large lengths can be transformed to an - // equally-valid witness from any other length, so we only have - // to check slice lengths from the "minimal sufficiently-large length" - // and below. + // Because of that, each witness for reachability-checking from one of the sufficiently-large + // lengths can be transformed to an equally-valid witness from any other length, so we all + // slice lengths from the "minimal sufficiently-large length" until infinity will behave the + // same. // - // Note that the fact that there is a *single* `sₘ` for each `m` - // not depending on the specific pattern in `P` is important: if + // Note that the fact that there is a *single* `sₘ` for each `m`, + // not depending on the specific pattern in `P`, is important: if // you look at the pair of patterns // `[true, ..]` // `[.., false]` // Then any slice of length ≥1 that matches one of these two // patterns can be trivially turned to a slice of any - // other length ≥1 that matches them and vice-versa - for + // other length ≥1 that matches them and vice-versa - // but the slice from length 2 `[false, true]` that matches neither // of these patterns can't be turned to a slice from length 1 that // matches neither of these patterns, so we have to consider @@ -1437,9 +1432,9 @@ where let mut max_suffix_len = 0; let mut max_fixed_len = 0; - for row in patterns { - match *row.kind { - PatKind::Constant { value } => { + for ctor in ctors { + match *ctor { + ConstantValue(value) => { // extract the length of an array/slice from a constant match (value.val, &value.ty.kind) { (_, ty::Array(_, n)) => { @@ -1451,13 +1446,12 @@ where _ => {} } } - PatKind::Slice { ref prefix, slice: None, ref suffix } => { - let fixed_len = prefix.len() as u64 + suffix.len() as u64; - max_fixed_len = cmp::max(max_fixed_len, fixed_len); + FixedLenSlice(len) => { + max_fixed_len = cmp::max(max_fixed_len, len); } - PatKind::Slice { ref prefix, slice: Some(_), ref suffix } => { - max_prefix_len = cmp::max(max_prefix_len, prefix.len() as u64); - max_suffix_len = cmp::max(max_suffix_len, suffix.len() as u64); + VarLenSlice(prefix, suffix) => { + max_prefix_len = cmp::max(max_prefix_len, prefix); + max_suffix_len = cmp::max(max_suffix_len, suffix); } _ => {} } @@ -1761,36 +1755,33 @@ pub fn is_useful<'p, 'a, 'tcx>( assert!(rows.iter().all(|r| r.len() == v.len())); - 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 - // be able to observe whether the types of the struct's fields are - // inhabited. - // - // If the field is truly inaccessible, then all the patterns - // matching against it must be wildcard patterns, so its type - // does not matter. - // - // However, if we are matching against non-wildcard patterns, we - // need to know the real type of the field so we can specialize - // against it. This primarily occurs through constants - they - // can include contents for fields that are inaccessible at the - // location of the match. In that case, the field's type is - // inhabited - by the constant - so we can just use it. - // - // FIXME: this might lead to "unstable" behavior with macro hygiene - // introducing uninhabited patterns for inaccessible fields. We - // need to figure out how to model that. - ty: matrix.heads().map(|p| p.ty).find(|ty| !ty.references_error()).unwrap_or(v.head().ty), - max_slice_length: max_slice_length(cx, matrix.heads().chain(Some(v.head()))), - }; + // TyErr is used to represent the type of wildcard patterns matching + // against inaccessible (private) fields of structs, so that we won't + // be able to observe whether the types of the struct's fields are + // inhabited. + // + // If the field is truly inaccessible, then all the patterns + // matching against it must be wildcard patterns, so its type + // does not matter. + // + // However, if we are matching against non-wildcard patterns, we + // need to know the real type of the field so we can specialize + // against it. This primarily occurs through constants - they + // can include contents for fields that are inaccessible at the + // location of the match. In that case, the field's type is + // inhabited - by the constant - so we can just use it. + // + // FIXME: this might lead to "unstable" behavior with macro hygiene + // introducing uninhabited patterns for inaccessible fields. We + // need to figure out how to model that. + let ty = matrix.heads().map(|p| p.ty).find(|ty| !ty.references_error()).unwrap_or(v.head().ty); - debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); + debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", ty, v.head()); - let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head(), pcx); + let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head(), ty); if cx.is_non_exhaustive_variant(v.head()) - && !cx.is_local(pcx.ty) + && !cx.is_local(ty) && !v_constructors.iter().any(|ctor| ctor.is_wildcard()) { debug!("is_useful - shortcut because declared non-exhaustive"); @@ -1800,11 +1791,14 @@ pub fn is_useful<'p, 'a, 'tcx>( let matrix_head_ctors: Vec> = matrix .heads() - .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, pcx)) + .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, ty)) .filter(|ctor| !ctor.is_wildcard()) .collect(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); + let max_slice_length = max_slice_length(cx, matrix_head_ctors.iter().chain(&v_constructors)); + let pcx = PatCtxt { ty, max_slice_length }; + v_constructors .into_iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &matrix_head_ctors)) @@ -1851,11 +1845,11 @@ fn pat_constructors<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, - pcx: PatCtxt<'tcx>, + ty: Ty<'tcx>, ) -> SmallVec<[Constructor<'tcx>; 1]> { match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { - pat_constructors(tcx, param_env, subpattern, pcx) + pat_constructors(tcx, param_env, subpattern, ty) } PatKind::Binding { .. } | PatKind::Wild => smallvec![Wildcard], PatKind::Leaf { .. } | PatKind::Deref { .. } => smallvec![Single], @@ -1869,9 +1863,9 @@ fn pat_constructors<'tcx>( lo.ty, end, )], - PatKind::Array { .. } => match pcx.ty.kind { + PatKind::Array { .. } => match ty.kind { ty::Array(_, length) => smallvec![FixedLenSlice(length.eval_usize(tcx, param_env))], - _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pcx.ty), + _ => span_bug!(pat.span, "bad ty {:?} for array pattern", ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let prefix = prefix.len() as u64; From c6d1cadc0cf3821c73df71c0b04beca352c88782 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 22:06:18 +0100 Subject: [PATCH 57/87] Inline max_slice_length Note that where we previously ran `max_slice_len` with input having not only matrix_head_ctors but also all of v_constructors. Now we run it on matrix_head_ctors and one constructor of v_constructors at a time. In the presence of or-patterns, those two things might differ. However the new behaviour is still valid with regards to the specified semantics of split_meta_constructor. --- src/librustc_mir/hair/pattern/_match.rs | 197 ++++++++++++------------ 1 file changed, 95 insertions(+), 102 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 05c621dcd157d..93d7333c1e8c3 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -640,7 +640,7 @@ impl<'tcx> Constructor<'tcx> { debug!("split_meta_constructor {:?}", self); assert!(!head_ctors.iter().any(|c| c.is_wildcard())); - match &self { + match self { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], ConstantRange(..) if should_treat_range_exhaustively(cx.tcx, &self) => { @@ -739,8 +739,99 @@ impl<'tcx> Constructor<'tcx> { .collect() } ConstantRange(..) => smallvec![self], - VarLenSlice(prefix, suffix) => { - (prefix + suffix..pcx.max_slice_length + 1).map(FixedLenSlice).collect() + VarLenSlice(self_prefix, self_suffix) => { + // A variable-length slice pattern is matched by an infinite collection of + // fixed-length array patterns. However it turns out that for each finite set of + // patterns `P`, all sufficiently large array lengths are equivalent. + // + // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies + // to exactly the subset `Pₜ` of `P` can be transformed to a slice + // `sₘ` for each sufficiently-large length `m` that applies to exactly + // the same subset of `P`. + // + // Because of that, each witness for reachability-checking from one of the + // sufficiently-large lengths can be transformed to an equally-valid witness from + // any other length, so we all slice lengths from the "minimal sufficiently-large + // length" until infinity will behave the same. + // + // Note that the fact that there is a *single* `sₘ` for each `m`, + // not depending on the specific pattern in `P`, is important: if + // you look at the pair of patterns + // `[true, ..]` + // `[.., false]` + // Then any slice of length ≥1 that matches one of these two + // patterns can be trivially turned to a slice of any + // other length ≥1 that matches them and vice-versa - + // but the slice from length 2 `[false, true]` that matches neither + // of these patterns can't be turned to a slice from length 1 that + // matches neither of these patterns, so we have to consider + // slices from length 2 there. + // + // Now, to see that that length exists and find it, observe that slice + // patterns are either "fixed-length" patterns (`[_, _, _]`) or + // "variable-length" patterns (`[_, .., _]`). + // + // For fixed-length patterns, all slices with lengths *longer* than + // the pattern's length have the same outcome (of not matching), so + // as long as `L` is greater than the pattern's length we can pick + // any `sₘ` from that length and get the same result. + // + // For variable-length patterns, the situation is more complicated, + // because as seen above the precise value of `sₘ` matters. + // + // However, for each variable-length pattern `p` with a prefix of length + // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last + // `slₚ` elements are examined. + // + // Therefore, as long as `L` is positive (to avoid concerns about empty + // types), all elements after the maximum prefix length and before + // the maximum suffix length are not examined by any variable-length + // pattern, and therefore can be added/removed without affecting + // them - creating equivalent patterns from any sufficiently-large + // length. + // + // Of course, if fixed-length patterns exist, we must be sure + // that our length is large enough to miss them all, so + // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})` + // + // For example, with the above pair of patterns, all elements + // but the first and last can be added/removed, so any + // witness of length ≥2 (say, `[false, false, true]`) can be + // turned to a witness from any other length ≥2. + + let mut max_prefix_len = self_prefix; + let mut max_suffix_len = self_suffix; + let mut max_fixed_len = 0; + + for ctor in head_ctors { + match *ctor { + ConstantValue(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) + } + _ => {} + } + } + FixedLenSlice(len) => { + max_fixed_len = cmp::max(max_fixed_len, len); + } + VarLenSlice(prefix, suffix) => { + max_prefix_len = cmp::max(max_prefix_len, prefix); + max_suffix_len = cmp::max(max_suffix_len, suffix); + } + _ => {} + } + } + + let max_slice_length = cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len); + + (self_prefix + self_suffix..=max_slice_length).map(FixedLenSlice).collect() } Wildcard => { let is_declared_nonexhaustive = @@ -1204,7 +1295,6 @@ pub enum WitnessPreference { #[derive(Copy, Clone, Debug)] struct PatCtxt<'tcx> { ty: Ty<'tcx>, - max_slice_length: u64, } /// A witness of non-exhaustiveness for error reporting, represented @@ -1364,102 +1454,6 @@ fn all_constructors<'a, 'tcx>( ctors } -fn max_slice_length<'p, 'a, 'tcx, I>(cx: &MatchCheckCtxt<'a, 'tcx>, ctors: I) -> u64 -where - I: Iterator>, - 'tcx: 'p, -{ - // A variable-length slice pattern is matched by an infinite collection of fixed-length array - // patterns. However it turns out that for each finite set of patterns `P`, all sufficiently - // large array lengths are equivalent. - // - // Each slice `s` with a "sufficiently-large" length `l ≥ L` that applies - // to exactly the subset `Pₜ` of `P` can be transformed to a slice - // `sₘ` for each sufficiently-large length `m` that applies to exactly - // the same subset of `P`. - // - // Because of that, each witness for reachability-checking from one of the sufficiently-large - // lengths can be transformed to an equally-valid witness from any other length, so we all - // slice lengths from the "minimal sufficiently-large length" until infinity will behave the - // same. - // - // Note that the fact that there is a *single* `sₘ` for each `m`, - // not depending on the specific pattern in `P`, is important: if - // you look at the pair of patterns - // `[true, ..]` - // `[.., false]` - // Then any slice of length ≥1 that matches one of these two - // patterns can be trivially turned to a slice of any - // other length ≥1 that matches them and vice-versa - - // but the slice from length 2 `[false, true]` that matches neither - // of these patterns can't be turned to a slice from length 1 that - // matches neither of these patterns, so we have to consider - // slices from length 2 there. - // - // Now, to see that that length exists and find it, observe that slice - // patterns are either "fixed-length" patterns (`[_, _, _]`) or - // "variable-length" patterns (`[_, .., _]`). - // - // For fixed-length patterns, all slices with lengths *longer* than - // the pattern's length have the same outcome (of not matching), so - // as long as `L` is greater than the pattern's length we can pick - // any `sₘ` from that length and get the same result. - // - // For variable-length patterns, the situation is more complicated, - // because as seen above the precise value of `sₘ` matters. - // - // However, for each variable-length pattern `p` with a prefix of length - // `plₚ` and suffix of length `slₚ`, only the first `plₚ` and the last - // `slₚ` elements are examined. - // - // Therefore, as long as `L` is positive (to avoid concerns about empty - // types), all elements after the maximum prefix length and before - // the maximum suffix length are not examined by any variable-length - // pattern, and therefore can be added/removed without affecting - // them - creating equivalent patterns from any sufficiently-large - // length. - // - // Of course, if fixed-length patterns exist, we must be sure - // that our length is large enough to miss them all, so - // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})` - // - // for example, with the above pair of patterns, all elements - // but the first and last can be added/removed, so any - // witness of length ≥2 (say, `[false, false, true]`) can be - // turned to a witness from any other length ≥2. - - let mut max_prefix_len = 0; - let mut max_suffix_len = 0; - let mut max_fixed_len = 0; - - for ctor in ctors { - match *ctor { - ConstantValue(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) - } - _ => {} - } - } - FixedLenSlice(len) => { - max_fixed_len = cmp::max(max_fixed_len, len); - } - VarLenSlice(prefix, suffix) => { - max_prefix_len = cmp::max(max_prefix_len, prefix); - max_suffix_len = cmp::max(max_suffix_len, suffix); - } - _ => {} - } - } - - cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len) -} - /// An inclusive interval, used for precise integer exhaustiveness checking. /// `IntRange`s always store a contiguous range. This means that values are /// encoded such that `0` encodes the minimum value for the integer, @@ -1796,8 +1790,7 @@ pub fn is_useful<'p, 'a, 'tcx>( .collect(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); - let max_slice_length = max_slice_length(cx, matrix_head_ctors.iter().chain(&v_constructors)); - let pcx = PatCtxt { ty, max_slice_length }; + let pcx = PatCtxt { ty }; v_constructors .into_iter() From 0c0fe9fa302945cd4843e8e1513d4fce0142286c Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 22:57:52 +0100 Subject: [PATCH 58/87] Add some test cases --- .../ui/pattern/usefulness/slice-patterns.rs | 14 ++++++++++++++ .../pattern/usefulness/slice-patterns.stderr | 18 +++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs index 97086c4d75ddc..e11f11ba7524d 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -93,4 +93,18 @@ fn main() { [false] => {} [..] => {} } + match s { + //~^ ERROR `&[_, _, true]` not covered + [] => {} + [_] => {} + [_, _] => {} + [.., false] => {} + } + match s { + //~^ ERROR `&[true, _, _]` not covered + [] => {} + [_] => {} + [_, _] => {} + [false, .., false] => {} + } } diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr index 3cea068543ebd..666e16627c57e 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -112,6 +112,22 @@ error: unreachable pattern LL | [false, true] => {} | ^^^^^^^^^^^^^ -error: aborting due to 15 previous errors +error[E0004]: non-exhaustive patterns: `&[_, _, true]` not covered + --> $DIR/slice-patterns.rs:96:11 + | +LL | match s { + | ^ pattern `&[_, _, true]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `&[true, _, _]` not covered + --> $DIR/slice-patterns.rs:103:11 + | +LL | match s { + | ^ pattern `&[true, _, _]` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 17 previous errors For more information about this error, try `rustc --explain E0004`. From 6b4426a2d9644bd182df3ea786e4445a456d5dac Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 5 Oct 2019 23:24:25 +0100 Subject: [PATCH 59/87] Splitting variable-length slices now respects the required invariant The previous behaviour did not respect the invariant specified in the description of the algorithm. As a nice side-effect, exhaustiveness errors have improved when slice_patterns is enabled; they now capture all missing lengths instead of the shortest. Note: this changes diagnostics, but only when slice_patterns is enabled. --- src/librustc_mir/hair/pattern/_match.rs | 31 +++++++++++++------ .../usefulness/match-slice-patterns.rs | 2 +- .../usefulness/match-slice-patterns.stderr | 4 +-- .../ui/pattern/usefulness/slice-patterns.rs | 12 +++---- .../pattern/usefulness/slice-patterns.stderr | 24 +++++++------- 5 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 93d7333c1e8c3..3190d099325ef 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -596,7 +596,7 @@ enum Constructor<'tcx> { impl<'tcx> Constructor<'tcx> { fn is_slice(&self) -> bool { match self { - FixedLenSlice { .. } => true, + FixedLenSlice(..) | VarLenSlice(..) => true, _ => false, } } @@ -792,12 +792,16 @@ impl<'tcx> Constructor<'tcx> { // // Of course, if fixed-length patterns exist, we must be sure // that our length is large enough to miss them all, so - // we can pick `L = max(FIXED_LEN+1 ∪ {max(PREFIX_LEN) + max(SUFFIX_LEN)})` + // we can pick `L = max(max(FIXED_LEN)+1, max(PREFIX_LEN) + max(SUFFIX_LEN))` // // For example, with the above pair of patterns, all elements // but the first and last can be added/removed, so any // witness of length ≥2 (say, `[false, false, true]`) can be // turned to a witness from any other length ≥2. + // + // For diagnostics, we keep the prefix and suffix lengths separate, so in the case + // where `max(FIXED_LEN)+1` is the largest, we adapt `max(PREFIX_LEN)` accordingly, + // so that `max(PREFIX_LEN) + max(SUFFIX_LEN) = L`. let mut max_prefix_len = self_prefix; let mut max_suffix_len = self_suffix; @@ -829,9 +833,14 @@ impl<'tcx> Constructor<'tcx> { } } - let max_slice_length = cmp::max(max_fixed_len + 1, max_prefix_len + max_suffix_len); + if max_fixed_len + 1 >= max_prefix_len + max_suffix_len { + max_prefix_len = cmp::max(max_prefix_len, max_fixed_len + 1 - max_suffix_len); + } - (self_prefix + self_suffix..=max_slice_length).map(FixedLenSlice).collect() + (self_prefix + self_suffix..max_prefix_len + max_suffix_len) + .map(FixedLenSlice) + .chain(Some(VarLenSlice(max_prefix_len, max_suffix_len))) + .collect() } Wildcard => { let is_declared_nonexhaustive = @@ -1020,7 +1029,8 @@ impl<'tcx> Constructor<'tcx> { Start => self_len, Boundary(n) => n + 1, }; - remaining_ctors.push(VarLenSlice(final_length, 0)); + // We know final_length >= self_len >= self_suffix + remaining_ctors.push(VarLenSlice(final_length - self_suffix, self_suffix)); } remaining_ctors @@ -1194,19 +1204,20 @@ impl<'tcx> Constructor<'tcx> { FixedLenSlice(_) => { PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } } - VarLenSlice(_, _) => match ty.kind { + VarLenSlice(prefix_len, _suffix_len) => match ty.kind { ty::Slice(ty) | ty::Array(ty, _) => { - let prefix = pats.collect(); if cx.tcx.features().slice_patterns { + let prefix = pats.by_ref().take(*prefix_len as usize).collect(); + let suffix = pats.collect(); let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; - PatKind::Slice { prefix, slice: Some(wild), suffix: vec![] } + PatKind::Slice { prefix, slice: Some(wild), suffix } } else { // We don't want to output a variable-length slice pattern if the // slice_patterns feature is not enabled. // The constructor covers infinitely many slice lengths, but for diagnostic // purposes it is correct to return only some examples of non-covered // patterns. So we just return the smallest length pattern here. - PatKind::Slice { prefix, slice: None, suffix: vec![] } + PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } } } _ => bug!("bad slice pattern {:?} {:?}", self, ty), @@ -2159,7 +2170,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( PatKind::Array { ref prefix, ref slice, ref suffix } | PatKind::Slice { ref prefix, ref slice, ref suffix } => match *constructor { - FixedLenSlice(..) => { + FixedLenSlice(..) | VarLenSlice(..) => { let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { diff --git a/src/test/ui/pattern/usefulness/match-slice-patterns.rs b/src/test/ui/pattern/usefulness/match-slice-patterns.rs index afbeb61e4415a..af7fd53a1f1e9 100644 --- a/src/test/ui/pattern/usefulness/match-slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/match-slice-patterns.rs @@ -2,7 +2,7 @@ fn check(list: &[Option<()>]) { match list { - //~^ ERROR `&[_, Some(_), None, _]` not covered + //~^ ERROR `&[_, Some(_), .., None, _]` not covered &[] => {}, &[_] => {}, &[_, _] => {}, diff --git a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr index 24769db34c932..72ae5d5fe3b33 100644 --- a/src/test/ui/pattern/usefulness/match-slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/match-slice-patterns.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: `&[_, Some(_), None, _]` not covered +error[E0004]: non-exhaustive patterns: `&[_, Some(_), .., None, _]` not covered --> $DIR/match-slice-patterns.rs:4:11 | LL | match list { - | ^^^^ pattern `&[_, Some(_), None, _]` not covered + | ^^^^ pattern `&[_, Some(_), .., None, _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/slice-patterns.rs b/src/test/ui/pattern/usefulness/slice-patterns.rs index e11f11ba7524d..da2d40caf1a40 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.rs +++ b/src/test/ui/pattern/usefulness/slice-patterns.rs @@ -37,7 +37,7 @@ fn main() { [.., false] => {} } match s { - //~^ ERROR `&[false, true]` not covered + //~^ ERROR `&[false, .., true]` not covered [] => {} [true, ..] => {} [.., false] => {} @@ -57,18 +57,18 @@ fn main() { [_] => {} } match s { - //~^ ERROR `&[false]` not covered + //~^ ERROR `&[false, ..]` not covered [] => {} [true, ..] => {} } match s { - //~^ ERROR `&[false, _]` not covered + //~^ ERROR `&[false, _, ..]` not covered [] => {} [_] => {} [true, ..] => {} } match s { - //~^ ERROR `&[_, false]` not covered + //~^ ERROR `&[_, .., false]` not covered [] => {} [_] => {} [.., true] => {} @@ -94,14 +94,14 @@ fn main() { [..] => {} } match s { - //~^ ERROR `&[_, _, true]` not covered + //~^ ERROR `&[_, _, .., true]` not covered [] => {} [_] => {} [_, _] => {} [.., false] => {} } match s { - //~^ ERROR `&[true, _, _]` not covered + //~^ ERROR `&[true, _, .., _]` not covered [] => {} [_] => {} [_, _] => {} diff --git a/src/test/ui/pattern/usefulness/slice-patterns.stderr b/src/test/ui/pattern/usefulness/slice-patterns.stderr index 666e16627c57e..6afe4705b0e69 100644 --- a/src/test/ui/pattern/usefulness/slice-patterns.stderr +++ b/src/test/ui/pattern/usefulness/slice-patterns.stderr @@ -14,11 +14,11 @@ LL | match s3 { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[false, true]` not covered +error[E0004]: non-exhaustive patterns: `&[false, .., true]` not covered --> $DIR/slice-patterns.rs:39:11 | LL | match s { - | ^ pattern `&[false, true]` not covered + | ^ pattern `&[false, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms @@ -46,27 +46,27 @@ LL | match s { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[false]` not covered +error[E0004]: non-exhaustive patterns: `&[false, ..]` not covered --> $DIR/slice-patterns.rs:59:11 | LL | match s { - | ^ pattern `&[false]` not covered + | ^ pattern `&[false, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[false, _]` not covered +error[E0004]: non-exhaustive patterns: `&[false, _, ..]` not covered --> $DIR/slice-patterns.rs:64:11 | LL | match s { - | ^ pattern `&[false, _]` not covered + | ^ pattern `&[false, _, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_, false]` not covered +error[E0004]: non-exhaustive patterns: `&[_, .., false]` not covered --> $DIR/slice-patterns.rs:70:11 | LL | match s { - | ^ pattern `&[_, false]` not covered + | ^ pattern `&[_, .., false]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms @@ -112,19 +112,19 @@ error: unreachable pattern LL | [false, true] => {} | ^^^^^^^^^^^^^ -error[E0004]: non-exhaustive patterns: `&[_, _, true]` not covered +error[E0004]: non-exhaustive patterns: `&[_, _, .., true]` not covered --> $DIR/slice-patterns.rs:96:11 | LL | match s { - | ^ pattern `&[_, _, true]` not covered + | ^ pattern `&[_, _, .., true]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[true, _, _]` not covered +error[E0004]: non-exhaustive patterns: `&[true, _, .., _]` not covered --> $DIR/slice-patterns.rs:103:11 | LL | match s { - | ^ pattern `&[true, _, _]` not covered + | ^ pattern `&[true, _, .., _]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From 72c3a68eee2d42a6dc34ea8aa579795463972d33 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Oct 2019 10:10:38 +0100 Subject: [PATCH 60/87] PatCtxt is not useful anymore It was introduced to carry max_slice_length so we don't need it anymore. --- src/librustc_mir/hair/pattern/_match.rs | 84 +++++++++---------------- 1 file changed, 29 insertions(+), 55 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 3190d099325ef..f9243acfb0e83 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -634,7 +634,7 @@ impl<'tcx> Constructor<'tcx> { fn split_meta_constructor( self, cx: &MatchCheckCtxt<'_, 'tcx>, - pcx: PatCtxt<'tcx>, + ty: Ty<'tcx>, head_ctors: &Vec>, ) -> SmallVec<[Constructor<'tcx>; 1]> { debug!("split_meta_constructor {:?}", self); @@ -735,7 +735,7 @@ impl<'tcx> Constructor<'tcx> { (Border::JustBefore(n), Border::AfterMax) => Some(n..=u128::MAX), (Border::AfterMax, _) => None, }) - .map(|range| IntRange::range_to_ctor(cx.tcx, pcx.ty, range)) + .map(|range| IntRange::range_to_ctor(cx.tcx, ty, range)) .collect() } ConstantRange(..) => smallvec![self], @@ -843,20 +843,19 @@ impl<'tcx> Constructor<'tcx> { .collect() } Wildcard => { - let is_declared_nonexhaustive = - !cx.is_local(pcx.ty) && cx.is_non_exhaustive_enum(pcx.ty); + let is_declared_nonexhaustive = !cx.is_local(ty) && cx.is_non_exhaustive_enum(ty); // `all_ctors` are all the constructors for the given type, which // should all be represented (or caught with the wild pattern `_`). - let all_ctors = all_constructors(cx, pcx); + let all_ctors = all_constructors(cx, ty); - let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(pcx.ty); + let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(ty); // 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() + || (ty.is_ptr_sized_integral() && !cx.tcx.features().precise_pointer_size_matching); // `missing_ctors` is the set of constructors from the same type as the @@ -880,13 +879,8 @@ impl<'tcx> Constructor<'tcx> { // Missing constructors are those that are not matched by any // non-wildcard patterns in the current column. - let missing_ctors = MissingConstructors::new( - pcx, - cx.tcx, - cx.param_env, - all_ctors, - head_ctors.clone(), - ); + let missing_ctors = + MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, head_ctors.clone()); debug!( "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", missing_ctors.is_empty(), @@ -920,7 +914,7 @@ impl<'tcx> Constructor<'tcx> { // contain any wildcards so we don't recurse infinitely. all_ctors .into_iter() - .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, head_ctors)) + .flat_map(|ctor| ctor.split_meta_constructor(cx, ty, head_ctors)) .collect() } } @@ -933,7 +927,6 @@ impl<'tcx> Constructor<'tcx> { /// notation). fn subtract_meta_constructor( self, - _pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, used_ctors: &Vec>, @@ -1170,7 +1163,6 @@ impl<'tcx> Constructor<'tcx> { fn apply<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, ty: Ty<'tcx>, pats: impl IntoIterator>, ) -> SmallVec<[Pat<'tcx>; 1]> { @@ -1236,7 +1228,7 @@ impl<'tcx> Constructor<'tcx> { // `Option::Some`, we get the pattern `Some(_)`. return missing_ctors .iter() - .flat_map(|ctor| ctor.apply_wildcards(cx, pcx, ty)) + .flat_map(|ctor| ctor.apply_wildcards(cx, ty)) .collect(); } }; @@ -1248,11 +1240,10 @@ impl<'tcx> Constructor<'tcx> { fn apply_wildcards<'a>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, ty: Ty<'tcx>, ) -> SmallVec<[Pat<'tcx>; 1]> { let pats = self.wildcard_subpatterns(cx, ty).rev(); - self.apply(cx, pcx, ty, pats) + self.apply(cx, ty, pats) } } @@ -1281,15 +1272,14 @@ impl<'tcx> Usefulness<'tcx> { fn apply_constructor( self, cx: &MatchCheckCtxt<'_, 'tcx>, - pcx: PatCtxt<'tcx>, ctor: &Constructor<'tcx>, - lty: Ty<'tcx>, + ty: Ty<'tcx>, ) -> Self { match self { UsefulWithWitness(witnesses) => UsefulWithWitness( witnesses .into_iter() - .flat_map(|witness| witness.apply_constructor(cx, pcx, &ctor, lty)) + .flat_map(|witness| witness.apply_constructor(cx, &ctor, ty)) .collect(), ), x => x, @@ -1303,11 +1293,6 @@ pub enum WitnessPreference { LeaveOutWitness, } -#[derive(Copy, Clone, Debug)] -struct PatCtxt<'tcx> { - ty: Ty<'tcx>, -} - /// A witness of non-exhaustiveness for error reporting, represented /// as a list of patterns (in reverse order of construction) with /// wildcards inside to represent elements that can take any inhabitant @@ -1365,7 +1350,6 @@ impl<'tcx> Witness<'tcx> { fn apply_constructor<'a>( mut self, cx: &MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, ctor: &Constructor<'tcx>, ty: Ty<'tcx>, ) -> SmallVec<[Self; 1]> { @@ -1373,7 +1357,7 @@ impl<'tcx> Witness<'tcx> { let applied_pats = { let len = self.0.len() as u64; let pats = self.0.drain((len - arity) as usize..).rev(); - ctor.apply(cx, pcx, ty, pats) + ctor.apply(cx, ty, pats) }; applied_pats @@ -1396,10 +1380,10 @@ impl<'tcx> Witness<'tcx> { /// `Option`, we do not include `Some(_)` in the returned list of constructors. fn all_constructors<'a, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, + ty: Ty<'tcx>, ) -> Vec> { - debug!("all_constructors({:?})", pcx.ty); - let ctors = match pcx.ty.kind { + debug!("all_constructors({:?})", ty); + let ctors = match ty.kind { ty::Bool => { [true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect() } @@ -1447,15 +1431,15 @@ fn all_constructors<'a, 'tcx>( let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; - vec![ConstantRange(min, max, pcx.ty, RangeEnd::Included)] + vec![ConstantRange(min, max, ty, RangeEnd::Included)] } ty::Uint(uty) => { let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let max = truncate(u128::max_value(), size); - vec![ConstantRange(0, max, pcx.ty, RangeEnd::Included)] + vec![ConstantRange(0, max, ty, RangeEnd::Included)] } _ => { - if cx.is_uninhabited(pcx.ty) { + if cx.is_uninhabited(ty) { vec![] } else { vec![Single] @@ -1648,9 +1632,8 @@ impl<'tcx> IntRange<'tcx> { // A struct to compute a set of constructors equivalent to `all_ctors \ used_ctors`. #[derive(Clone)] struct MissingConstructors<'tcx> { - pcx: PatCtxt<'tcx>, - tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, + tcx: TyCtxt<'tcx>, all_ctors: Vec>, used_ctors: Vec>, } @@ -1663,13 +1646,12 @@ type MissingConstructorsIter<'a, 'tcx, F> = std::iter::FlatMap< impl<'tcx> MissingConstructors<'tcx> { fn new( - pcx: PatCtxt<'tcx>, tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, all_ctors: Vec>, used_ctors: Vec>, ) -> Self { - MissingConstructors { pcx, tcx, param_env, all_ctors, used_ctors } + MissingConstructors { tcx, param_env, all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { @@ -1690,12 +1672,7 @@ impl<'tcx> MissingConstructors<'tcx> { impl FnMut(&'a Constructor<'tcx>) -> SmallVec<[Constructor<'tcx>; 1]>, > { self.all_ctors.iter().flat_map(move |req_ctor| { - req_ctor.clone().subtract_meta_constructor( - self.pcx, - self.tcx, - self.param_env, - &self.used_ctors, - ) + req_ctor.clone().subtract_meta_constructor(self.tcx, self.param_env, &self.used_ctors) }) } } @@ -1801,12 +1778,10 @@ pub fn is_useful<'p, 'a, 'tcx>( .collect(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); - let pcx = PatCtxt { ty }; - v_constructors .into_iter() - .flat_map(|ctor| ctor.split_meta_constructor(cx, pcx, &matrix_head_ctors)) - .map(|c| is_useful_specialized(cx, pcx, matrix, v, c, pcx.ty, witness_preference)) + .flat_map(|ctor| ctor.split_meta_constructor(cx, ty, &matrix_head_ctors)) + .map(|c| is_useful_specialized(cx, matrix, v, c, ty, witness_preference)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) } @@ -1815,23 +1790,22 @@ pub fn is_useful<'p, 'a, 'tcx>( /// to the specialised version of both the pattern matrix `M` and the new pattern `q`. fn is_useful_specialized<'p, 'a, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, - pcx: PatCtxt<'tcx>, matrix: &Matrix<'p, 'tcx>, v: &PatStack<'_, 'tcx>, ctor: Constructor<'tcx>, - lty: Ty<'tcx>, + ty: Ty<'tcx>, witness_preference: WitnessPreference, ) -> Usefulness<'tcx> { - debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, lty); + debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, ty); - let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, lty).collect(); + let ctor_wild_subpatterns_owned: Vec<_> = ctor.wildcard_subpatterns(cx, ty).collect(); let ctor_wild_subpatterns: Vec<_> = ctor_wild_subpatterns_owned.iter().collect(); let matrix = matrix.specialize(cx, &ctor, &ctor_wild_subpatterns); let ret = v .specialize(cx, &ctor, &ctor_wild_subpatterns) .into_iter() .map(|v| is_useful(cx, &matrix, &v, witness_preference)) - .map(|u| u.apply_constructor(cx, pcx, &ctor, lty)) + .map(|u| u.apply_constructor(cx, &ctor, ty)) .find(|result| result.is_useful()) .unwrap_or(NotUseful); ret From f5d833fc4be72e6e41f667f986a4d57c4cbd0aa9 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Oct 2019 00:04:37 +0100 Subject: [PATCH 61/87] Cleanup comments Several comments were leftover from before the rework of the algorithm and didn't make sens anymore. --- src/librustc_mir/hair/pattern/_match.rs | 148 ++++++++++-------------- 1 file changed, 61 insertions(+), 87 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index f9243acfb0e83..37c481528e216 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -510,7 +510,7 @@ pub struct MatchCheckCtxt<'a, 'tcx> { /// checking inhabited-ness of types because whether a type is (visibly) /// inhabited can depend on whether it was defined in the current module or /// not. E.g., `struct Foo { _private: ! }` cannot be seen to be empty - /// outside it's module and should not be matchable with an empty match + /// outside its module and should not be matchable with an empty match /// statement. pub module: DefId, param_env: ty::ParamEnv<'tcx>, @@ -565,7 +565,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } -/// Constructors and metaconstructors. +/// Constructors, including base constructors and metaconstructors. #[derive(Clone, Debug, PartialEq)] enum Constructor<'tcx> { // Base constructors @@ -584,12 +584,12 @@ enum Constructor<'tcx> { ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), /// Slice patterns. Captures any array constructor of length >= i+j. VarLenSlice(u64, u64), - /// Wildcard metaconstructor. + /// Wildcard metaconstructor. Captures all possible constructors for a given type. Wildcard, - /// List of constructors that were _not_ present in the first column - /// of the matrix when encountering a wildcard. The contained list must - /// be nonempty. - /// This is only used in the output of splitting the wildcard metaconstructor. + /// Special wildcard-like constructor that carries only a subset of all possible constructors. + /// It is used only when splitting `Constructor::Wildcard` and some constructors were not + /// present in the matrix. + /// The contained list must be nonempty. MissingConstructors(MissingConstructors<'tcx>), } @@ -629,8 +629,7 @@ impl<'tcx> Constructor<'tcx> { /// Split a constructor into equivalence classes of constructors that behave the same /// for the given matrix. See description of the algorithm for details. - /// Note: We can rely on this returning an empty list if the type is uninhabited and - /// we're not in the privately_empty case. + /// Note: We can rely on this returning an empty list if the type is (visibly) uninhabited. fn split_meta_constructor( self, cx: &MatchCheckCtxt<'_, 'tcx>, @@ -644,28 +643,20 @@ impl<'tcx> Constructor<'tcx> { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], ConstantRange(..) if should_treat_range_exhaustively(cx.tcx, &self) => { - // For exhaustive integer matching, some constructors are grouped within other - // constructors (namely integer typed values are grouped within ranges). However, - // when specialising these constructors, we want to be specialising for the - // underlying constructors (the integers), not the groups (the ranges). Thus we - // need to split the groups up. Splitting them up naïvely would mean creating a - // separate constructor for every single value in the range, which is clearly - // impractical. However, observe that for some ranges of integers, the - // specialisation will be identical across all values in that range (i.e., there - // are equivalence classes of ranges of constructors based on their - // `is_useful_specialized` outcome). These classes are grouped by the patterns that - // apply to them (in the matrix `P`). We can split the range whenever the patterns - // that apply to that range (specifically: the patterns that *intersect* with that - // range) change. - // Our solution, therefore, is to split the range constructor into subranges at - // every single point the group of intersecting patterns changes (using the method - // described below). And voilà! We're testing precisely those ranges that we need - // to, without any exhaustive matching on actual integers. The nice thing about - // this is that the number of subranges is linear in the number of rows in the - // matrix (i.e., the number of cases in the `match` statement), so we don't need to - // be worried about matching over gargantuan ranges. + // Splitting up a range naïvely would mean creating a separate constructor for + // every single value in the range, which is clearly impractical. We therefore want + // to keep together subranges for which the specialisation will be identical across + // all values in that range. These classes are grouped by the patterns that apply + // to them (in the matrix `M`). We can split the range whenever the patterns that + // apply to that range (specifically: the patterns that *intersect* with that + // range) change. Our solution, therefore, is to split the range constructor into + // subranges at every single point where the group of intersecting patterns changes + // (using the method described below). The nice thing about this is that the number + // of subranges is linear in the number of rows in the matrix (i.e., the number of + // cases in the `match` statement), so we don't need to be worried about matching + // over a gargantuan number of ranges. // - // Essentially, given the first column of a matrix representing ranges, looking + // Essentially, given the first column of a matrix representing ranges, that looks // like the following: // // |------| |----------| |-------| || @@ -680,12 +671,10 @@ impl<'tcx> Constructor<'tcx> { // The logic for determining how to split the ranges is fairly straightforward: we // calculate boundaries for each interval range, sort them, then create // constructors for each new interval between every pair of boundary points. (This - // essentially sums up to performing the intuitive merging operation depicted + // essentially amounts to performing the intuitive merging operation depicted // above.) - // We only care about finding all the subranges within the range of the constructor - // range. Anything else is irrelevant, because it is guaranteed to result in - // `NotUseful`, which is the default case anyway, and can be ignored. + // We only care about finding all the subranges within the range of `self`. let ctor_range = IntRange::from_ctor(cx.tcx, cx.param_env, &self).unwrap(); /// Represents a border between 2 integers. Because the intervals spanning borders @@ -810,7 +799,7 @@ impl<'tcx> Constructor<'tcx> { for ctor in head_ctors { match *ctor { ConstantValue(value) => { - // extract the length of an array/slice from a constant + // Extract the length of an array/slice from a constant match (value.val, &value.ty.kind) { (_, ty::Array(_, n)) => { max_fixed_len = @@ -845,14 +834,19 @@ impl<'tcx> Constructor<'tcx> { Wildcard => { let is_declared_nonexhaustive = !cx.is_local(ty) && cx.is_non_exhaustive_enum(ty); - // `all_ctors` are all the constructors for the given type, which - // should all be represented (or caught with the wild pattern `_`). + // `all_ctors` is the list of all the constructors for the given type. let all_ctors = all_constructors(cx, ty); let is_privately_empty = all_ctors.is_empty() && !cx.is_uninhabited(ty); // 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. + // See the `match_privately_empty` test for details. + // + // FIXME: currently the only way I know of something can + // be a privately-empty enum is when the exhaustive_patterns + // feature flag is not present, so this is only + // needed for that case. let is_non_exhaustive = is_privately_empty || is_declared_nonexhaustive || (ty.is_ptr_sized_integral() @@ -865,20 +859,6 @@ impl<'tcx> Constructor<'tcx> { // Therefore, if there is some pattern that is unmatched by `matrix`, // it will still be unmatched if the first constructor is replaced by // any of the constructors in `missing_ctors` - // - // However, if our scrutinee is *privately* an empty enum, we - // must treat it as though it had an "unknown" constructor (in - // that case, all other patterns obviously can't be variants) - // to avoid exposing its emptyness. See the `match_privately_empty` - // test for details. - // - // FIXME: currently the only way I know of something can - // be a privately-empty enum is when the exhaustive_patterns - // feature flag is not present, so this is only - // needed for that case. - - // Missing constructors are those that are not matched by any - // non-wildcard patterns in the current column. let missing_ctors = MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, head_ctors.clone()); debug!( @@ -932,11 +912,12 @@ impl<'tcx> Constructor<'tcx> { used_ctors: &Vec>, ) -> SmallVec<[Constructor<'tcx>; 1]> { debug!("subtract_meta_constructor {:?}", self); + // The input must not contain a wildcard assert!(!used_ctors.iter().any(|c| c.is_wildcard())); match self { - // Those constructors can't match a non-wildcard metaconstructor, so we're fine - // just comparing for equality. + // Those constructors can't intersect with a non-wildcard metaconstructor, so we're + // fine just comparing for equality. Single | Variant(_) => { if used_ctors.iter().any(|c| c == &self) { smallvec![] @@ -953,11 +934,11 @@ impl<'tcx> Constructor<'tcx> { if used_ctors.iter().any(overlaps) { smallvec![] } else { smallvec![self] } } VarLenSlice(self_prefix, self_suffix) => { - let self_len = self_prefix + self_suffix; // Initially we cover all slice lengths starting from self_len. + let self_len = self_prefix + self_suffix; // If there is a VarLenSlice(n) in used_ctors, then we have to discard - // all lengths >= n. So we pick the smallest one. + // all lengths >= n. So we pick the smallest such n. let max_len: Option<_> = used_ctors .iter() .filter_map(|c: &Constructor<'tcx>| match c { @@ -978,7 +959,7 @@ impl<'tcx> Constructor<'tcx> { // individual FixedLenSlice lengths in used_ctors. For that, // we extract all those lengths that are in our remaining range and // sort them. Every such length becomes a boundary between ranges - // of lengths that will remain. + // of the lengths that will remain. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum Length { Start, @@ -988,10 +969,12 @@ impl<'tcx> Constructor<'tcx> { let mut lengths: Vec<_> = used_ctors .iter() + // Extract fixed-size lengths .filter_map(|c: &Constructor<'tcx>| match c { FixedLenSlice(other_len) => Some(*other_len), _ => None, }) + // Keep only those in the remaining range .filter(|l| *l >= self_len) .filter(|l| match max_len { Some(max_len) => *l < max_len, @@ -1004,6 +987,7 @@ impl<'tcx> Constructor<'tcx> { lengths.sort_unstable(); lengths.dedup(); + // For each adjacent pair of lengths, output the lengths in between. let mut remaining_ctors: SmallVec<_> = lengths .windows(2) .flat_map(|window| match (window[0], window[1]) { @@ -1022,7 +1006,7 @@ impl<'tcx> Constructor<'tcx> { Start => self_len, Boundary(n) => n + 1, }; - // We know final_length >= self_len >= self_suffix + // We know final_length >= self_len >= self_suffix so this can't underflow. remaining_ctors.push(VarLenSlice(final_length - self_suffix, self_suffix)); } @@ -1127,10 +1111,10 @@ impl<'tcx> Constructor<'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. + /// is the number of its arguments. /// - /// 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. + /// For instance, a tuple pattern `(_, 42, Some([]))` has arity 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 *self { @@ -1342,10 +1326,10 @@ impl<'tcx> Witness<'tcx> { /// of values, V, where each value in that set is not covered by any previously /// used patterns and is covered by the pattern P'. Examples: /// - /// left_ty: tuple of 3 elements + /// ty: tuple of 3 elements /// pats: [10, 20, _] => (10, 20, _) /// - /// left_ty: struct X { a: (bool, &'static str), b: usize} + /// ty: struct X { a: (bool, &'static str), b: usize} /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 } fn apply_constructor<'a>( mut self, @@ -1372,9 +1356,8 @@ impl<'tcx> Witness<'tcx> { } /// This determines the set of all possible constructors of a pattern matching -/// values of type `left_ty`. For vectors, this would normally be an infinite set -/// but is instead bounded by the maximum fixed length of slice patterns in -/// the column of patterns being analyzed. +/// values of type `ty`. We possibly return metaconstructors like integer ranges +/// that capture several base constructors at once. /// /// We make sure to omit constructors that are statically impossible. E.g., for /// `Option`, we do not include `Some(_)` in the returned list of constructors. @@ -1691,23 +1674,22 @@ impl<'tcx> PartialEq for MissingConstructors<'tcx> { } } -/// Algorithm from http://moscova.inria.fr/~maranget/papers/warn/index.html. -/// The algorithm from the paper has been modified to correctly handle empty -/// types. The changes are: +/// Main entrypoint of the algorithm described at th top of the file. +/// Note that to correctly handle empty types: /// (0) We don't exit early if the pattern matrix has zero rows. We just /// continue to recurse over columns. /// (1) all_constructors will only return constructors that are statically /// possible. E.g., it will only return `Ok` for `Result`. /// -/// This finds whether a (row) vector `v` of patterns is 'useful' in relation -/// to a set of such vectors `m` - this is defined as there being a set of -/// inputs that will match `v` but not any of the sets in `m`. +/// This finds whether a pattern-stack `v` is 'useful' in relation to a set of such pattern-stacks +/// (aka 'matrix') `m` - this is defined as there being a set of inputs that will match `v` but not +/// any of the rows in `m`. /// /// All the patterns at each column of the `matrix ++ v` matrix must /// have the same type, except that wildcard (PatKind::Wild) patterns /// with type `TyErr` are also allowed, even if the "type of the column" /// is not `TyErr`. That is used to represent private fields, as using their -/// real type would assert that they are inhabited. +/// real type might leak that they are inhabited. /// /// This is used both for reachability checking (if a pattern isn't useful in /// relation to preceding patterns, it is not reachable) and exhaustiveness @@ -1811,14 +1793,8 @@ fn is_useful_specialized<'p, 'a, 'tcx>( ret } -/// Determines the constructors that the given pattern can be specialized to. -/// -/// In most cases, there's only one constructor that a specific pattern -/// represents, such as a specific enum variant or a specific literal value. -/// Slice patterns, however, can match slices of different lengths. For instance, -/// `[a, b, tail @ ..]` can match a slice of length 2, 3, 4 and so on. -/// -/// Returns `None` in case of a catch-all, which can't be specialized. +/// Determines the constructors that are covered by the given pattern. +/// Except for or-patterns, this returns only one constructor (possibly a meta-constructor). fn pat_constructors<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -2014,13 +1990,11 @@ fn patterns_for_variant<'p, 'tcx>( PatStack::from_vec(result) } -/// 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. +/// 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 vec![] if the pattern does not have +/// the given constructor. See the top of the file for details. /// -/// 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_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( @@ -2034,7 +2008,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( } if let MissingConstructors(_) = constructor { - // By construction of MissingConstructors, we know that all non-wildcard constructors + // By the invariant of MissingConstructors, we know that all non-wildcard constructors // should be discarded. return match *pat.kind { PatKind::Binding { .. } | PatKind::Wild => smallvec![PatStack::empty()], From 94227f58ef617eaa184cc84edd4607a07d547395 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Oct 2019 16:04:32 +0100 Subject: [PATCH 62/87] Store Const in ConstantRange This makes the conversions from/to Const better localized. In particular, eval_bits only happens in IntRange::from_ctor. --- src/librustc_mir/hair/pattern/_match.rs | 83 +++++++++++-------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 37c481528e216..6230d8ecf30a8 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -581,7 +581,7 @@ enum Constructor<'tcx> { // Meta-constructors /// Ranges of literal values (`2..=5` and `2..5`). - ConstantRange(u128, u128, Ty<'tcx>, RangeEnd), + ConstantRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), /// Slice patterns. Captures any array constructor of length >= i+j. VarLenSlice(u64, u64), /// Wildcard metaconstructor. Captures all possible constructors for a given type. @@ -1151,7 +1151,7 @@ impl<'tcx> Constructor<'tcx> { pats: impl IntoIterator>, ) -> SmallVec<[Pat<'tcx>; 1]> { let mut pats = pats.into_iter(); - let pat = match self { + let pat = match *self { Single | Variant(_) => match ty.kind { ty::Adt(..) | ty::Tuple(..) => { let pats = pats @@ -1183,7 +1183,7 @@ impl<'tcx> Constructor<'tcx> { VarLenSlice(prefix_len, _suffix_len) => match ty.kind { ty::Slice(ty) | ty::Array(ty, _) => { if cx.tcx.features().slice_patterns { - let prefix = pats.by_ref().take(*prefix_len as usize).collect(); + let prefix = pats.by_ref().take(prefix_len as usize).collect(); let suffix = pats.collect(); let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; PatKind::Slice { prefix, slice: Some(wild), suffix } @@ -1199,13 +1199,9 @@ impl<'tcx> Constructor<'tcx> { _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, 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: *end, - }), + ConstantRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), Wildcard => PatKind::Wild, - MissingConstructors(missing_ctors) => { + MissingConstructors(ref missing_ctors) => { // Construct for each missing constructor a "wildcard" version of this // constructor, that matches everything that can be built with // it. For example, if `ctor` is a `Constructor::Variant` for @@ -1394,32 +1390,28 @@ fn all_constructors<'a, 'tcx>( .map(|v| Variant(v.def_id)) .collect(), ty::Char => { + let param_env = ty::ParamEnv::empty().and(cx.tcx.types.char); + let to_const = |x| ty::Const::from_bits(cx.tcx, x as u128, param_env); vec![ // The valid Unicode Scalar Value ranges. - ConstantRange( - '\u{0000}' as u128, - '\u{D7FF}' as u128, - cx.tcx.types.char, - RangeEnd::Included, - ), - ConstantRange( - '\u{E000}' as u128, - '\u{10FFFF}' as u128, - cx.tcx.types.char, - RangeEnd::Included, - ), + ConstantRange(to_const('\u{0000}'), to_const('\u{D7FF}'), RangeEnd::Included), + ConstantRange(to_const('\u{E000}'), to_const('\u{10FFFF}'), RangeEnd::Included), ] } ty::Int(ity) => { + let param_env = ty::ParamEnv::empty().and(ty); + let to_const = |x| ty::Const::from_bits(cx.tcx, x, param_env); let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; - vec![ConstantRange(min, max, ty, RangeEnd::Included)] + vec![ConstantRange(to_const(min), to_const(max), RangeEnd::Included)] } ty::Uint(uty) => { + let param_env = ty::ParamEnv::empty().and(ty); + let to_const = |x| ty::Const::from_bits(cx.tcx, x, param_env); let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let max = truncate(u128::max_value(), size); - vec![ConstantRange(0, max, ty, RangeEnd::Included)] + vec![ConstantRange(to_const(0), to_const(max), RangeEnd::Included)] } _ => { if cx.is_uninhabited(ty) { @@ -1501,11 +1493,14 @@ impl<'tcx> IntRange<'tcx> { #[inline] fn from_range( tcx: TyCtxt<'tcx>, - lo: u128, - hi: u128, - ty: Ty<'tcx>, + param_env: ty::ParamEnv<'tcx>, + lo: &Const<'tcx>, + hi: &Const<'tcx>, end: &RangeEnd, ) -> Option> { + let ty = lo.ty; + let lo = lo.eval_bits(tcx, param_env, lo.ty); + let hi = hi.eval_bits(tcx, param_env, hi.ty); if Self::is_integral(ty) { // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. @@ -1531,7 +1526,7 @@ impl<'tcx> IntRange<'tcx> { // Floating-point ranges are permitted and we don't want // to consider them when constructing integer ranges. match ctor { - ConstantRange(lo, hi, ty, end) => Self::from_range(tcx, *lo, *hi, ty, end), + ConstantRange(lo, hi, end) => Self::from_range(tcx, param_env, lo, hi, end), ConstantValue(val) => Self::from_const(tcx, param_env, val), _ => None, } @@ -1560,7 +1555,9 @@ impl<'tcx> IntRange<'tcx> { let ty = ty::ParamEnv::empty().and(ty); ConstantValue(ty::Const::from_bits(tcx, lo ^ bias, ty)) } else { - ConstantRange(lo ^ bias, hi ^ bias, ty, RangeEnd::Included) + let param_env = ty::ParamEnv::empty().and(ty); + let to_const = |x| ty::Const::from_bits(tcx, x, param_env); + ConstantRange(to_const(lo ^ bias), to_const(hi ^ bias), RangeEnd::Included) } } @@ -1811,12 +1808,7 @@ fn pat_constructors<'tcx>( smallvec![Variant(adt_def.variants[variant_index].def_id)] } PatKind::Constant { value } => smallvec![ConstantValue(value)], - PatKind::Range(PatRange { lo, hi, end }) => smallvec![ConstantRange( - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), - lo.ty, - end, - )], + PatKind::Range(PatRange { lo, hi, end }) => smallvec![ConstantRange(lo, hi, end)], PatKind::Array { .. } => match ty.kind { ty::Array(_, length) => smallvec![FixedLenSlice(length.eval_usize(tcx, param_env))], _ => span_bug!(pat.span, "bad ty {:?} for array pattern", ty), @@ -1901,7 +1893,7 @@ fn slice_pat_covered_by_const<'tcx>( fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) -> bool { let ty = match ctor { ConstantValue(value) => value.ty, - ConstantRange(_, _, ty, _) => ty, + ConstantRange(lo, _, _) => lo.ty, _ => return false, }; if let ty::Char | ty::Int(_) | ty::Uint(_) = ty.kind { @@ -1921,12 +1913,7 @@ fn constructor_intersects_pattern<'p, 'tcx>( if should_treat_range_exhaustively(tcx, ctor) { let range = match *pat.kind { PatKind::Constant { value } => ConstantValue(value), - PatKind::Range(PatRange { lo, hi, end }) => ConstantRange( - lo.eval_bits(tcx, param_env, lo.ty), - hi.eval_bits(tcx, param_env, hi.ty), - lo.ty, - end, - ), + PatKind::Range(PatRange { lo, hi, end }) => ConstantRange(lo, hi, end), _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), }; @@ -1942,11 +1929,12 @@ fn constructor_intersects_pattern<'p, 'tcx>( // Fallback for non-ranges and ranges that involve floating-point numbers, which are not // conveniently handled by `IntRange`. For these cases, the constructor may not be a range // so intersection actually devolves into being covered by the pattern. - let (from, to, end, ty) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty), + let (from, to, end) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end), _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), }; + let ty = from.ty; trace!("constructor_intersects_pattern {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); let cmp_from = |c_from| compare_const_vals(tcx, c_from, from, param_env, ty); let cmp_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); @@ -1958,10 +1946,9 @@ fn constructor_intersects_pattern<'p, 'tcx>( (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); (from != Ordering::Less) && end } - ConstantRange(from, to, ty, range_end) => { - let to = cmp_to(ty::Const::from_bits(tcx, to, ty::ParamEnv::empty().and(ty)))?; - let from = - cmp_from(ty::Const::from_bits(tcx, from, ty::ParamEnv::empty().and(ty)))?; + ConstantRange(from, to, range_end) => { + let to = cmp_to(to)?; + let from = cmp_from(from)?; let end = (to == Ordering::Less) || (end == range_end && to == Ordering::Equal); (from != Ordering::Less) && end } From 60021ce8f8dbbf3777449e2232ee64d4bbe1a04f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sun, 6 Oct 2019 16:56:27 +0100 Subject: [PATCH 63/87] Cleanup constructor_intersects_pattern --- src/librustc_mir/hair/pattern/_match.rs | 39 ++++++++++--------------- 1 file changed, 15 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 6230d8ecf30a8..4d5532fe5af04 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1910,7 +1910,10 @@ fn constructor_intersects_pattern<'p, 'tcx>( ctor: &Constructor<'tcx>, pat: &'p Pat<'tcx>, ) -> Option> { - if should_treat_range_exhaustively(tcx, ctor) { + trace!("constructor_intersects_pattern {:#?}, {:#?}", ctor, pat); + if let Single = ctor { + Some(PatStack::default()) + } else if should_treat_range_exhaustively(tcx, ctor) { let range = match *pat.kind { PatKind::Constant { value } => ConstantValue(value), PatKind::Range(PatRange { lo, hi, end }) => ConstantRange(lo, hi, end), @@ -1929,34 +1932,22 @@ fn constructor_intersects_pattern<'p, 'tcx>( // Fallback for non-ranges and ranges that involve floating-point numbers, which are not // conveniently handled by `IntRange`. For these cases, the constructor may not be a range // so intersection actually devolves into being covered by the pattern. - let (from, to, end) = match *pat.kind { + let (pat_from, pat_to, pat_end) = match *pat.kind { PatKind::Constant { value } => (value, value, RangeEnd::Included), PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end), _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), }; - let ty = from.ty; - trace!("constructor_intersects_pattern {:#?}, {:#?}, {:#?}, {}", ctor, from, to, ty); - let cmp_from = |c_from| compare_const_vals(tcx, c_from, from, param_env, ty); - let cmp_to = |c_to| compare_const_vals(tcx, c_to, to, param_env, ty); - let result = match *ctor { - ConstantValue(value) => { - let to = cmp_to(value)?; - let from = cmp_from(value)?; - let end = - (to == Ordering::Less) || (end == RangeEnd::Included && to == Ordering::Equal); - (from != Ordering::Less) && end - } - ConstantRange(from, to, range_end) => { - let to = cmp_to(to)?; - let from = cmp_from(from)?; - let end = (to == Ordering::Less) || (end == range_end && to == Ordering::Equal); - (from != Ordering::Less) && end - } - Single => true, - _ => bug!(), + let (ctor_from, ctor_to, ctor_end) = match *ctor { + ConstantValue(value) => (value, value, RangeEnd::Included), + ConstantRange(from, to, range_end) => (from, to, range_end), + _ => bug!("`constructor_intersects_pattern` called with {:?}", ctor), }; - - if result { Some(PatStack::default()) } else { None } + let order_to = compare_const_vals(tcx, ctor_to, pat_to, param_env, pat_from.ty)?; + let order_from = compare_const_vals(tcx, ctor_from, pat_from, param_env, pat_from.ty)?; + let intersects = (order_from != Ordering::Less) + && ((order_to == Ordering::Less) + || (pat_end == ctor_end && order_to == Ordering::Equal)); + if intersects { Some(PatStack::default()) } else { None } } } From 3d0945d88f67589c25b2340dc8c271b1c6424841 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 7 Oct 2019 17:19:18 +0100 Subject: [PATCH 64/87] Implement nnethercote's suggestion https://github.com/rust-lang/rust/pull/65160#commitcomment-35382077 --- src/librustc_mir/hair/pattern/_match.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 4d5532fe5af04..20f8202da10c5 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1904,6 +1904,8 @@ fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) } /// Checks whether there exists any shared value in either `ctor` or `pat` by intersecting them. +// This has a single call site that can be hot +#[inline(always)] fn constructor_intersects_pattern<'p, 'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, From 5badbb76d77273b5fc23ba1f6d8b439201ce0a03 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 7 Oct 2019 17:33:20 +0100 Subject: [PATCH 65/87] Implement Centril's suggestion https://github.com/rust-lang/rust/pull/65160#discussion_r331811572 --- src/librustc_mir/hair/pattern/_match.rs | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 20f8202da10c5..03cb2778b3f07 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1154,24 +1154,19 @@ impl<'tcx> Constructor<'tcx> { let pat = match *self { Single | Variant(_) => match ty.kind { ty::Adt(..) | ty::Tuple(..) => { - let pats = pats + let subpatterns = 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 } + match ty.kind { + ty::Adt(adt_def, substs) if adt_def.is_enum() => PatKind::Variant { + adt_def, + substs, + variant_index: self.variant_index_for_adt(cx, adt_def), + subpatterns, + }, + _ => PatKind::Leaf { subpatterns }, } } ty::Ref(..) => PatKind::Deref { subpattern: pats.nth(0).unwrap() }, From defc66d979833c208c72ebe641a848a47976125c Mon Sep 17 00:00:00 2001 From: Nadrieril Feneanar Date: Fri, 11 Oct 2019 12:15:11 +0100 Subject: [PATCH 66/87] Incorporate varkor's review --- src/librustc_mir/hair/pattern/_match.rs | 76 +++++++++++++++++-------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 03cb2778b3f07..7142abcf161f6 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -340,12 +340,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { pub fn from_pattern(pat: &'p Pat<'tcx>) -> Self { PatStack(smallvec![pat]) } + fn empty() -> Self { PatStack(smallvec![]) } + 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)) } @@ -353,9 +356,11 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { fn is_empty(&self) -> bool { self.0.is_empty() } + fn len(&self) -> usize { self.0.len() } + fn head<'a, 'p2>(&'a self) -> &'p2 Pat<'tcx> where 'p: 'p2, @@ -605,7 +610,7 @@ impl<'tcx> Constructor<'tcx> { match self { Wildcard => true, MissingConstructors(_) => bug!( - "Not sure if MissingConstructors should be a wildcard. Shouldn't happen anyways." + "not sure if MissingConstructors should be a wildcard; shouldn't happen anyways." ), _ => false, } @@ -934,6 +939,32 @@ impl<'tcx> Constructor<'tcx> { if used_ctors.iter().any(overlaps) { smallvec![] } else { smallvec![self] } } VarLenSlice(self_prefix, self_suffix) => { + // Assume we have the following match: + // ``` + // match slice { + // [0] => {} + // [_, _, _] => {} + // [1, 2, 3, 4, 5, 6, ..] => {} + // [_, _, _, _, _, _, _, _] => {} + // [0, ..] => {} + // } + // ``` + // We want to know which constructors are matched by the last pattern, but are not + // matched by the first four ones. Since we only speak of constructors here, we + // only care about the length of the slices and not the subpatterns. + // For that, we first notice that because of the third pattern, all constructors of + // lengths 6 or more are covered already. `max_len` will be `Some(6)`. + // Then we'll look at constructors of lengths < 6 to see which are missing. We can + // ignore pattern 4 because it's longer than 6. We are left with patterns 1 and 2. + // The `length` vector will therefore contain `[Start, Boundary(1), Boundary(3), + // Boundary(6)]`. + // The resulting list of remaining constructors will be those strictly between + // those boundaries. Knowing that `self_len` is 1, we get `[FixedLenSlice(2), + // FixedLenSlice(4), FixedLenSlice(5)]`. + // Notice that `FixedLenSlice(0)` is not covered by any of the patterns here, but + // we don't care because we only want constructors that _are_ matched by the last + // pattern. + // Initially we cover all slice lengths starting from self_len. let self_len = self_prefix + self_suffix; @@ -962,7 +993,7 @@ impl<'tcx> Constructor<'tcx> { // of the lengths that will remain. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] enum Length { - Start, + Start, // `Start` will be sorted before `Boundary` Boundary(u64), } use Length::*; @@ -1000,7 +1031,7 @@ impl<'tcx> Constructor<'tcx> { // If there was a max_len, then we're done. Otherwise, we // still need to include all lengths starting from the longest - // one til infinity, using VarLenSlice. + // one until infinity, using VarLenSlice. if max_len.is_none() { let final_length = match lengths.last().unwrap() { Start => self_len, @@ -1137,13 +1168,15 @@ 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>, @@ -1659,10 +1692,12 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { } } -/// The implementation panics because this should not happen +/// This is needed for the `PartialEq` impl of `Constructor`. +/// Comparing a `Constructor::MissingConstructor` with something else +/// should however never happen, so this implementaiton panics. impl<'tcx> PartialEq for MissingConstructors<'tcx> { fn eq(&self, _other: &Self) -> bool { - bug!("Tried to compare MissingConstructors for equality") + bug!("tried to compare MissingConstructors for equality") } } @@ -1982,18 +2017,13 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( pat = subpattern; } - if let MissingConstructors(_) = constructor { - // By the invariant of MissingConstructors, we know that all non-wildcard constructors - // should be discarded. - return match *pat.kind { - PatKind::Binding { .. } | PatKind::Wild => smallvec![PatStack::empty()], - _ => smallvec![], - }; - } else if let Wildcard = constructor { - // If we get here, either there were only wildcards in the first component of the - // matrix, or we are in a special non_exhaustive case where we pretend the type has - // an extra `_` constructor to prevent exhaustive matching. In both cases, all - // non-wildcard constructors should be discarded. + if let Wildcard | MissingConstructors(_) = constructor { + // If `constructor` is `Wildcard`: either there were only wildcards in the first component + // of the matrix, or we are in a special non_exhaustive case where we pretend the type has + // an extra `_` constructor to prevent exhaustive matching. In both cases, all non-wildcard + // constructors should be discarded. + // If `constructor` is `MissingConstructors(_)`: by the invariant of MissingConstructors, + // we know that all non-wildcard constructors should be discarded. return match *pat.kind { PatKind::Binding { .. } | PatKind::Wild => smallvec![PatStack::empty()], _ => smallvec![], From d8b71d49faa6761825b7421fd5ac9d14a75dcac1 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 11 Oct 2019 14:26:34 +0100 Subject: [PATCH 67/87] Be more consistent in terminology --- 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 7142abcf161f6..2477e664d0cca 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -166,22 +166,22 @@ /// is the wildcard `_`, which stands for all the constructors of a given type. /// /// In practice, the meta-constructors we make use of in this file are the following: -/// - any normal constructor is also a metaconstructor with exactly one member; +/// - any normal constructor is also a meta-constructor with exactly one member; /// - the wildcard `_`, that captures all constructors of a given type; /// - the constant range `x..y` that captures a range of values for types that support /// it, like integers; /// - the variable-length slice `[x, y, .., z]`, that captures all slice constructors /// from a given length onwards; -/// - the "missing constructors" metaconstructor, that captures a provided arbitrary group +/// - the "missing constructors" meta-constructor, that captures a provided arbitrary group /// of constructors. /// -/// We first redefine `pat_constructors` to potentially return a metaconstructor when relevant +/// We first redefine `pat_constructors` to potentially return a meta-constructor when relevant /// for a pattern. /// -/// We then add a step to the algorithm: a function `split_metaconstructor(mc, M)` that returns -/// a list of metaconstructors, with the following properties: +/// We then add a step to the algorithm: a function `split_meta_constructor(mc, M)` that returns +/// a list of meta-constructors, with the following properties: /// - the set of base constructors covered by the output must be the same as covered by `mc`; -/// - for each metaconstructor `k` in the output, all the `c ϵ k` behave the same relative +/// - for each meta-constructor `k` in the output, all the `c ϵ k` behave the same relative /// to `M`. More precisely, we want that for any two `c1` and `c2` in `k`, /// `U(S(c1, M), S(c1, p))` iff `U(S(c2, M), S(c2, p))`; /// - if the first column of `M` is only wildcards, then the function returns at most @@ -207,7 +207,7 @@ /// Thus we get the new inductive step (i.e. when `n > 0`): /// `U(M, p) := /// ∃(mc ϵ pat_constructors(p_1)) -/// ∃(mc' ϵ split_metaconstructor(mc, M)) +/// ∃(mc' ϵ split_meta-constructor(mc, M)) /// U(S(c, M), S(c, p)) for some c ϵ mc'` /// Note: in the case of an uninhabited type, there won't be any `mc'` so this just returns false. /// @@ -570,7 +570,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } -/// Constructors, including base constructors and metaconstructors. +/// Constructors, including base constructors and meta-constructors. #[derive(Clone, Debug, PartialEq)] enum Constructor<'tcx> { // Base constructors @@ -589,7 +589,7 @@ enum Constructor<'tcx> { ConstantRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), /// Slice patterns. Captures any array constructor of length >= i+j. VarLenSlice(u64, u64), - /// Wildcard metaconstructor. Captures all possible constructors for a given type. + /// Wildcard meta-constructor. Captures all possible constructors for a given type. Wildcard, /// Special wildcard-like constructor that carries only a subset of all possible constructors. /// It is used only when splitting `Constructor::Wildcard` and some constructors were not @@ -882,13 +882,13 @@ impl<'tcx> Constructor<'tcx> { } else if !missing_ctors.is_empty() { if head_ctors.is_empty() { // If head_ctors is empty, then all constructors of the type behave the same - // so we can keep the Wildcard metaconstructor. + // so we can keep the Wildcard meta-constructor. smallvec![Wildcard] } else { // Otherwise, we have a set of missing constructors that is neither empty // not equal to all_constructors. Since all missing constructors will // behave the same (i.e. will be matched only by wildcards), we return a - // metaconstructor that contains all of them at once. + // meta-constructor that contains all of them at once. smallvec![MissingConstructors(missing_ctors)] } } else { @@ -921,7 +921,7 @@ impl<'tcx> Constructor<'tcx> { assert!(!used_ctors.iter().any(|c| c.is_wildcard())); match self { - // Those constructors can't intersect with a non-wildcard metaconstructor, so we're + // Those constructors can't intersect with a non-wildcard meta-constructor, so we're // fine just comparing for equality. Single | Variant(_) => { if used_ctors.iter().any(|c| c == &self) { @@ -1380,7 +1380,7 @@ impl<'tcx> Witness<'tcx> { } /// This determines the set of all possible constructors of a pattern matching -/// values of type `ty`. We possibly return metaconstructors like integer ranges +/// values of type `ty`. We possibly return meta-constructors like integer ranges /// that capture several base constructors at once. /// /// We make sure to omit constructors that are statically impossible. E.g., for From 66dac52ccc02e4c1287dbb4701c8cd018251e2dc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 11 Oct 2019 16:36:15 +0100 Subject: [PATCH 68/87] Move range exhaustiveness check to IntRange::intersection Only IntRange needs to worry about range exhaustiveness really. --- src/librustc_mir/hair/pattern/_match.rs | 48 +++++++++++++++---------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 2477e664d0cca..870ba3e0fbfe0 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -647,7 +647,9 @@ impl<'tcx> Constructor<'tcx> { match self { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], - ConstantRange(..) if should_treat_range_exhaustively(cx.tcx, &self) => { + ConstantRange(ref lo, ..) + if IntRange::should_treat_range_exhaustively(cx.tcx, lo.ty) => + { // Splitting up a range naïvely would mean creating a separate constructor for // every single value in the range, which is clearly impractical. We therefore want // to keep together subranges for which the specialisation will be identical across @@ -707,7 +709,7 @@ impl<'tcx> Constructor<'tcx> { let row_borders = head_ctors .iter() .flat_map(|ctor| IntRange::from_ctor(cx.tcx, cx.param_env, ctor)) - .flat_map(|range| ctor_range.intersection(&range)) + .flat_map(|range| ctor_range.intersection(cx.tcx, &range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); @@ -1477,6 +1479,13 @@ impl<'tcx> IntRange<'tcx> { } } + fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { + // Don't treat `usize`/`isize` exhaustively unless the `precise_pointer_size_matching` + // feature is enabled. + IntRange::is_integral(ty) + && (!ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching) + } + #[inline] fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> { match ty.kind { @@ -1625,14 +1634,19 @@ impl<'tcx> IntRange<'tcx> { remaining_ranges } - fn intersection(&self, other: &Self) -> Option { + fn intersection(&self, tcx: TyCtxt<'tcx>, other: &Self) -> Option { let ty = self.ty; let (lo, hi) = (*self.range.start(), *self.range.end()); let (other_lo, other_hi) = (*other.range.start(), *other.range.end()); - if lo <= other_hi && other_lo <= hi { - Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty }) + if Self::should_treat_range_exhaustively(tcx, ty) { + if lo <= other_hi && other_lo <= hi { + Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), ty }) + } else { + None + } } else { - None + // If the range sould not be treated exhaustively, fallback to checking for inclusion. + if other_lo <= lo && hi <= other_hi { Some(self.clone()) } else { None } } } } @@ -1918,19 +1932,14 @@ fn slice_pat_covered_by_const<'tcx>( Ok(true) } -// Whether to evaluate a constructor using exhaustive integer matching. This is true if the -// constructor is a range or constant with an integer type. -fn should_treat_range_exhaustively(tcx: TyCtxt<'tcx>, ctor: &Constructor<'tcx>) -> bool { +// Whether a constructor is a range or constant with an integer type. +fn is_integral_range(ctor: &Constructor<'tcx>) -> bool { let ty = match ctor { ConstantValue(value) => value.ty, ConstantRange(lo, _, _) => lo.ty, _ => return false, }; - if let ty::Char | ty::Int(_) | ty::Uint(_) = ty.kind { - !ty.is_ptr_sized_integral() || tcx.features().precise_pointer_size_matching - } else { - false - } + IntRange::is_integral(ty) } /// Checks whether there exists any shared value in either `ctor` or `pat` by intersecting them. @@ -1945,7 +1954,7 @@ fn constructor_intersects_pattern<'p, 'tcx>( trace!("constructor_intersects_pattern {:#?}, {:#?}", ctor, pat); if let Single = ctor { Some(PatStack::default()) - } else if should_treat_range_exhaustively(tcx, ctor) { + } else if is_integral_range(ctor) { let range = match *pat.kind { PatKind::Constant { value } => ConstantValue(value), PatKind::Range(PatRange { lo, hi, end }) => ConstantRange(lo, hi, end), @@ -1954,11 +1963,14 @@ fn constructor_intersects_pattern<'p, 'tcx>( let pat = IntRange::from_ctor(tcx, param_env, &range)?; let ctor = IntRange::from_ctor(tcx, param_env, ctor)?; - ctor.intersection(&pat)?; + ctor.intersection(tcx, &pat)?; + // Constructor splitting should ensure that all intersections we encounter are actually + // inclusions. 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); + Some(PatStack::default()) } else { // Fallback for non-ranges and ranges that involve floating-point numbers, which are not @@ -1976,10 +1988,10 @@ fn constructor_intersects_pattern<'p, 'tcx>( }; let order_to = compare_const_vals(tcx, ctor_to, pat_to, param_env, pat_from.ty)?; let order_from = compare_const_vals(tcx, ctor_from, pat_from, param_env, pat_from.ty)?; - let intersects = (order_from != Ordering::Less) + let included = (order_from != Ordering::Less) && ((order_to == Ordering::Less) || (pat_end == ctor_end && order_to == Ordering::Equal)); - if intersects { Some(PatStack::default()) } else { None } + if included { Some(PatStack::default()) } else { None } } } From b85e4be74f6efd6bff3e9e3d353a582fce0dc397 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 11 Oct 2019 17:35:06 +0100 Subject: [PATCH 69/87] Introduce IntRange meta-constructor --- src/librustc_mir/hair/pattern/_match.rs | 116 +++++++++++++++++++----- 1 file changed, 93 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 870ba3e0fbfe0..8571bad1378ee 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -585,7 +585,9 @@ enum Constructor<'tcx> { FixedLenSlice(u64), // Meta-constructors - /// Ranges of literal values (`2..=5` and `2..5`). + /// Ranges of integer literal values (`2..=5` and `2..5`). + IntRange(IntRange<'tcx>), + /// Ranges of non-integer literal values (`2.0..=5.2`). ConstantRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd), /// Slice patterns. Captures any array constructor of length >= i+j. VarLenSlice(u64, u64), @@ -647,8 +649,8 @@ impl<'tcx> Constructor<'tcx> { match self { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], - ConstantRange(ref lo, ..) - if IntRange::should_treat_range_exhaustively(cx.tcx, lo.ty) => + ConstantRange(..) | IntRange(..) + if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => { // Splitting up a range naïvely would mean creating a separate constructor for // every single value in the range, which is clearly impractical. We therefore want @@ -734,7 +736,7 @@ impl<'tcx> Constructor<'tcx> { .map(|range| IntRange::range_to_ctor(cx.tcx, ty, range)) .collect() } - ConstantRange(..) => smallvec![self], + ConstantRange(..) | IntRange(..) => smallvec![self], VarLenSlice(self_prefix, self_suffix) => { // A variable-length slice pattern is matched by an infinite collection of // fixed-length array patterns. However it turns out that for each finite set of @@ -1045,7 +1047,7 @@ impl<'tcx> Constructor<'tcx> { remaining_ctors } - ConstantRange(..) | ConstantValue(..) => { + ConstantRange(..) | ConstantValue(..) | IntRange(..) => { let mut remaining_ctors = smallvec![self]; // For each used ctor, subtract from the current set of constructors. @@ -1137,7 +1139,11 @@ impl<'tcx> Constructor<'tcx> { ty::Slice(ty) | ty::Array(ty, _) => (0..prefix + suffix).map(|_| ty).collect(), _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, - ConstantValue(_) | MissingConstructors(_) | ConstantRange(..) | Wildcard => vec![], + ConstantValue(_) + | MissingConstructors(_) + | ConstantRange(..) + | IntRange(..) + | Wildcard => vec![], }; subpattern_types.into_iter().map(|ty| Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }) @@ -1162,7 +1168,11 @@ impl<'tcx> Constructor<'tcx> { }, FixedLenSlice(length) => length, VarLenSlice(prefix, suffix) => prefix + suffix, - ConstantValue(_) | ConstantRange(..) | Wildcard | MissingConstructors(_) => 0, + ConstantValue(_) + | ConstantRange(..) + | IntRange(..) + | Wildcard + | MissingConstructors(_) => 0, } } @@ -1230,6 +1240,7 @@ impl<'tcx> Constructor<'tcx> { }, ConstantValue(value) => PatKind::Constant { value }, ConstantRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }), + IntRange(ref range) => range.to_patkind(cx.tcx), Wildcard => PatKind::Wild, MissingConstructors(ref missing_ctors) => { // Construct for each missing constructor a "wildcard" version of this @@ -1424,8 +1435,26 @@ fn all_constructors<'a, 'tcx>( let to_const = |x| ty::Const::from_bits(cx.tcx, x as u128, param_env); vec![ // The valid Unicode Scalar Value ranges. - ConstantRange(to_const('\u{0000}'), to_const('\u{D7FF}'), RangeEnd::Included), - ConstantRange(to_const('\u{E000}'), to_const('\u{10FFFF}'), RangeEnd::Included), + IntRange( + IntRange::from_range( + cx.tcx, + cx.param_env, + to_const('\u{0000}'), + to_const('\u{D7FF}'), + &RangeEnd::Included, + ) + .unwrap(), + ), + IntRange( + IntRange::from_range( + cx.tcx, + cx.param_env, + to_const('\u{E000}'), + to_const('\u{10FFFF}'), + &RangeEnd::Included, + ) + .unwrap(), + ), ] } ty::Int(ity) => { @@ -1434,14 +1463,32 @@ fn all_constructors<'a, 'tcx>( let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; - vec![ConstantRange(to_const(min), to_const(max), RangeEnd::Included)] + vec![IntRange( + IntRange::from_range( + cx.tcx, + cx.param_env, + to_const(min), + to_const(max), + &RangeEnd::Included, + ) + .unwrap(), + )] } ty::Uint(uty) => { let param_env = ty::ParamEnv::empty().and(ty); let to_const = |x| ty::Const::from_bits(cx.tcx, x, param_env); let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let max = truncate(u128::max_value(), size); - vec![ConstantRange(to_const(0), to_const(max), RangeEnd::Included)] + vec![IntRange( + IntRange::from_range( + cx.tcx, + cx.param_env, + to_const(0), + to_const(max), + &RangeEnd::Included, + ) + .unwrap(), + )] } _ => { if cx.is_uninhabited(ty) { @@ -1464,7 +1511,7 @@ fn all_constructors<'a, 'tcx>( /// /// `IntRange` is never used to encode an empty range or a "range" that wraps /// around the (offset) space: i.e., `range.lo <= range.hi`. -#[derive(Clone)] +#[derive(Debug, Clone, PartialEq)] struct IntRange<'tcx> { pub range: RangeInclusive, pub ty: Ty<'tcx>, @@ -1563,6 +1610,7 @@ impl<'tcx> IntRange<'tcx> { // Floating-point ranges are permitted and we don't want // to consider them when constructing integer ranges. match ctor { + IntRange(range) => Some(range.clone()), ConstantRange(lo, hi, end) => Self::from_range(tcx, param_env, lo, hi, end), ConstantValue(val) => Self::from_const(tcx, param_env, val), _ => None, @@ -1580,21 +1628,30 @@ impl<'tcx> IntRange<'tcx> { } } - /// Converts a `RangeInclusive` to a `ConstantValue` or inclusive `ConstantRange`. + /// Converts a `RangeInclusive` to a `Constructor`. fn range_to_ctor( - tcx: TyCtxt<'tcx>, + _tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, - r: RangeInclusive, + range: RangeInclusive, ) -> Constructor<'tcx> { - let bias = IntRange::signed_bias(tcx, ty); - let (lo, hi) = r.into_inner(); + IntRange(IntRange { ty, range }) + } + + /// Converts an `IntRange` to a `PatKind::Constant` or inclusive `PatKind::Range`. + fn to_patkind(&self, tcx: TyCtxt<'tcx>) -> PatKind<'tcx> { + let bias = IntRange::signed_bias(tcx, self.ty); + let (lo, hi) = self.range.clone().into_inner(); if lo == hi { - let ty = ty::ParamEnv::empty().and(ty); - ConstantValue(ty::Const::from_bits(tcx, lo ^ bias, ty)) + let ty = ty::ParamEnv::empty().and(self.ty); + PatKind::Constant { value: ty::Const::from_bits(tcx, lo ^ bias, ty) } } else { - let param_env = ty::ParamEnv::empty().and(ty); + let param_env = ty::ParamEnv::empty().and(self.ty); let to_const = |x| ty::Const::from_bits(tcx, x, param_env); - ConstantRange(to_const(lo ^ bias), to_const(hi ^ bias), RangeEnd::Included) + PatKind::Range(PatRange { + lo: to_const(lo ^ bias), + hi: to_const(hi ^ bias), + end: RangeEnd::Included, + }) } } @@ -1851,8 +1908,20 @@ fn pat_constructors<'tcx>( PatKind::Variant { adt_def, variant_index, .. } => { smallvec![Variant(adt_def.variants[variant_index].def_id)] } - PatKind::Constant { value } => smallvec![ConstantValue(value)], - PatKind::Range(PatRange { lo, hi, end }) => smallvec![ConstantRange(lo, hi, end)], + PatKind::Constant { value } => { + if let Some(range) = IntRange::from_const(tcx, param_env, value) { + smallvec![IntRange(range)] + } else { + smallvec![ConstantValue(value)] + } + } + PatKind::Range(PatRange { lo, hi, end }) => { + if let Some(range) = IntRange::from_range(tcx, param_env, &lo, &hi, &end) { + smallvec![IntRange(range)] + } else { + smallvec![ConstantRange(lo, hi, end)] + } + } PatKind::Array { .. } => match ty.kind { ty::Array(_, length) => smallvec![FixedLenSlice(length.eval_usize(tcx, param_env))], _ => span_bug!(pat.span, "bad ty {:?} for array pattern", ty), @@ -1935,6 +2004,7 @@ fn slice_pat_covered_by_const<'tcx>( // Whether a constructor is a range or constant with an integer type. fn is_integral_range(ctor: &Constructor<'tcx>) -> bool { let ty = match ctor { + IntRange(_) => return true, ConstantValue(value) => value.ty, ConstantRange(lo, _, _) => lo.ty, _ => return false, From eea0b79d804f6c37cc4218d43ff910ab83de052d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 11 Oct 2019 17:55:59 +0100 Subject: [PATCH 70/87] We no longer construct any ConstantValue or ConstantRange for non-integral types --- src/librustc_mir/hair/pattern/_match.rs | 48 ++++++++----------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 8571bad1378ee..46383680d7f73 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -649,9 +649,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], - ConstantRange(..) | IntRange(..) - if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => - { + IntRange(..) if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => { // Splitting up a range naïvely would mean creating a separate constructor for // every single value in the range, which is clearly impractical. We therefore want // to keep together subranges for which the specialisation will be identical across @@ -736,6 +734,7 @@ impl<'tcx> Constructor<'tcx> { .map(|range| IntRange::range_to_ctor(cx.tcx, ty, range)) .collect() } + // When not treated exhaustively, don't split ranges. ConstantRange(..) | IntRange(..) => smallvec![self], VarLenSlice(self_prefix, self_suffix) => { // A variable-length slice pattern is matched by an infinite collection of @@ -927,12 +926,8 @@ impl<'tcx> Constructor<'tcx> { match self { // Those constructors can't intersect with a non-wildcard meta-constructor, so we're // fine just comparing for equality. - Single | Variant(_) => { - if used_ctors.iter().any(|c| c == &self) { - smallvec![] - } else { - smallvec![self] - } + Single | Variant(_) | ConstantRange(..) | ConstantValue(..) => { + if used_ctors.iter().any(|c| c == &self) { smallvec![] } else { smallvec![self] } } FixedLenSlice(self_len) => { let overlaps = |c: &Constructor<'_>| match c { @@ -1047,7 +1042,7 @@ impl<'tcx> Constructor<'tcx> { remaining_ctors } - ConstantRange(..) | ConstantValue(..) | IntRange(..) => { + IntRange(..) => { let mut remaining_ctors = smallvec![self]; // For each used ctor, subtract from the current set of constructors. @@ -1603,16 +1598,12 @@ impl<'tcx> IntRange<'tcx> { } fn from_ctor( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + _tcx: TyCtxt<'tcx>, + _param_env: ty::ParamEnv<'tcx>, ctor: &Constructor<'tcx>, ) -> Option> { - // Floating-point ranges are permitted and we don't want - // to consider them when constructing integer ranges. match ctor { IntRange(range) => Some(range.clone()), - ConstantRange(lo, hi, end) => Self::from_range(tcx, param_env, lo, hi, end), - ConstantValue(val) => Self::from_const(tcx, param_env, val), _ => None, } } @@ -2001,17 +1992,6 @@ fn slice_pat_covered_by_const<'tcx>( Ok(true) } -// Whether a constructor is a range or constant with an integer type. -fn is_integral_range(ctor: &Constructor<'tcx>) -> bool { - let ty = match ctor { - IntRange(_) => return true, - ConstantValue(value) => value.ty, - ConstantRange(lo, _, _) => lo.ty, - _ => return false, - }; - IntRange::is_integral(ty) -} - /// Checks whether there exists any shared value in either `ctor` or `pat` by intersecting them. // This has a single call site that can be hot #[inline(always)] @@ -2024,21 +2004,21 @@ fn constructor_intersects_pattern<'p, 'tcx>( trace!("constructor_intersects_pattern {:#?}, {:#?}", ctor, pat); if let Single = ctor { Some(PatStack::default()) - } else if is_integral_range(ctor) { - let range = match *pat.kind { - PatKind::Constant { value } => ConstantValue(value), - PatKind::Range(PatRange { lo, hi, end }) => ConstantRange(lo, hi, end), + } else if let IntRange(ctor) = ctor { + let pat = match *pat.kind { + PatKind::Constant { value } => IntRange::from_const(tcx, param_env, value)?, + PatKind::Range(PatRange { lo, hi, end }) => { + IntRange::from_range(tcx, param_env, lo, hi, &end)? + } _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), }; - let pat = IntRange::from_ctor(tcx, param_env, &range)?; - let ctor = IntRange::from_ctor(tcx, param_env, ctor)?; ctor.intersection(tcx, &pat)?; // Constructor splitting should ensure that all intersections we encounter are actually // inclusions. let (pat_lo, pat_hi) = pat.range.into_inner(); - let (ctor_lo, ctor_hi) = ctor.range.into_inner(); + let (ctor_lo, ctor_hi) = ctor.range.clone().into_inner(); assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); Some(PatStack::default()) From 34b0774e1f4d8060bcc5ccaa1d29c4df74e30e5f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 11 Oct 2019 23:35:26 +0100 Subject: [PATCH 71/87] Remove some unnecessary conversions between Constructor and IntRange --- src/librustc_mir/hair/pattern/_match.rs | 73 ++++++++----------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 46383680d7f73..4212def0c5c2f 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -649,7 +649,7 @@ impl<'tcx> Constructor<'tcx> { match self { // Any base constructor can be used unchanged. Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], - IntRange(..) if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => { + IntRange(ctor_range) if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => { // Splitting up a range naïvely would mean creating a separate constructor for // every single value in the range, which is clearly impractical. We therefore want // to keep together subranges for which the specialisation will be identical across @@ -681,9 +681,6 @@ impl<'tcx> Constructor<'tcx> { // essentially amounts to performing the intuitive merging operation depicted // above.) - // We only care about finding all the subranges within the range of `self`. - let ctor_range = IntRange::from_ctor(cx.tcx, cx.param_env, &self).unwrap(); - /// Represents a border between 2 integers. Because the intervals spanning borders /// must be able to cover every integer, we need to be able to represent /// 2^128 + 1 such borders. @@ -708,7 +705,7 @@ impl<'tcx> Constructor<'tcx> { // class lies between 2 borders. let row_borders = head_ctors .iter() - .flat_map(|ctor| IntRange::from_ctor(cx.tcx, cx.param_env, ctor)) + .flat_map(IntRange::from_ctor) .flat_map(|range| ctor_range.intersection(cx.tcx, &range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); @@ -731,7 +728,8 @@ impl<'tcx> Constructor<'tcx> { (Border::JustBefore(n), Border::AfterMax) => Some(n..=u128::MAX), (Border::AfterMax, _) => None, }) - .map(|range| IntRange::range_to_ctor(cx.tcx, ty, range)) + .map(|range| IntRange::new(ty, range)) + .map(IntRange) .collect() } // When not treated exhaustively, don't split ranges. @@ -915,8 +913,8 @@ impl<'tcx> Constructor<'tcx> { /// notation). fn subtract_meta_constructor( self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + _tcx: TyCtxt<'tcx>, + _param_env: ty::ParamEnv<'tcx>, used_ctors: &Vec>, ) -> SmallVec<[Constructor<'tcx>; 1]> { debug!("subtract_meta_constructor {:?}", self); @@ -1042,31 +1040,25 @@ impl<'tcx> Constructor<'tcx> { remaining_ctors } - IntRange(..) => { - let mut remaining_ctors = smallvec![self]; + IntRange(range) => { + let used_ranges = used_ctors.iter().flat_map(IntRange::from_ctor); + let mut remaining_ranges: SmallVec<[IntRange<'tcx>; 1]> = smallvec![range]; // For each used ctor, subtract from the current set of constructors. - for used_ctor in used_ctors { - remaining_ctors = remaining_ctors + for used_range in used_ranges { + remaining_ranges = remaining_ranges .into_iter() - .filter(|ctor| ctor != used_ctor) - .flat_map(|ctor| -> SmallVec<[Constructor<'tcx>; 2]> { - if let Some(interval) = IntRange::from_ctor(tcx, param_env, used_ctor) { - interval.subtract_from(tcx, param_env, ctor) - } else { - smallvec![ctor] - } - }) + .flat_map(|range| used_range.subtract_from(range)) .collect(); // If the constructors that have been considered so far already cover // the entire range of `self`, no need to look at more constructors. - if remaining_ctors.is_empty() { + if remaining_ranges.is_empty() { break; } } - remaining_ctors + remaining_ranges.into_iter().map(IntRange).collect() } Wildcard | MissingConstructors(_) => { bug!("shouldn't try to subtract constructor {:?}", self) @@ -1513,6 +1505,10 @@ struct IntRange<'tcx> { } impl<'tcx> IntRange<'tcx> { + fn new(ty: Ty<'tcx>, range: RangeInclusive) -> Self { + IntRange { ty, range } + } + #[inline] fn is_integral(ty: Ty<'_>) -> bool { match ty.kind { @@ -1597,11 +1593,7 @@ impl<'tcx> IntRange<'tcx> { } } - fn from_ctor( - _tcx: TyCtxt<'tcx>, - _param_env: ty::ParamEnv<'tcx>, - ctor: &Constructor<'tcx>, - ) -> Option> { + fn from_ctor(ctor: &Constructor<'tcx>) -> Option> { match ctor { IntRange(range) => Some(range.clone()), _ => None, @@ -1619,15 +1611,6 @@ impl<'tcx> IntRange<'tcx> { } } - /// Converts a `RangeInclusive` to a `Constructor`. - fn range_to_ctor( - _tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - range: RangeInclusive, - ) -> Constructor<'tcx> { - IntRange(IntRange { ty, range }) - } - /// Converts an `IntRange` to a `PatKind::Constant` or inclusive `PatKind::Range`. fn to_patkind(&self, tcx: TyCtxt<'tcx>) -> PatKind<'tcx> { let bias = IntRange::signed_bias(tcx, self.ty); @@ -1648,16 +1631,8 @@ impl<'tcx> IntRange<'tcx> { /// Returns a collection of ranges that spans the values covered by `ctor`, subtracted /// by the values covered by `self`: i.e., `ctor \ self` (in set notation). - fn subtract_from( - &self, - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ctor: Constructor<'tcx>, - ) -> SmallVec<[Constructor<'tcx>; 2]> { - let range = match IntRange::from_ctor(tcx, param_env, &ctor) { - None => return smallvec![], - Some(int_range) => int_range.range, - }; + fn subtract_from(&self, other: Self) -> SmallVec<[Self; 2]> { + let range = other.range; let ty = self.ty; let (lo, hi) = (*self.range.start(), *self.range.end()); @@ -1666,17 +1641,17 @@ impl<'tcx> IntRange<'tcx> { if lo > range_hi || range_lo > hi { // The pattern doesn't intersect with the range at all, // so the range remains untouched. - remaining_ranges.push(Self::range_to_ctor(tcx, ty, range_lo..=range_hi)); + remaining_ranges.push(Self::new(ty, range_lo..=range_hi)); } else { if lo > range_lo { // The pattern intersects an upper section of the // range, so a lower section will remain. - remaining_ranges.push(Self::range_to_ctor(tcx, ty, range_lo..=(lo - 1))); + remaining_ranges.push(Self::new(ty, range_lo..=(lo - 1))); } if hi < range_hi { // The pattern intersects a lower section of the // range, so an upper section will remain. - remaining_ranges.push(Self::range_to_ctor(tcx, ty, (hi + 1)..=range_hi)); + remaining_ranges.push(Self::new(ty, (hi + 1)..=range_hi)); } } remaining_ranges From 087adfefbb37e1a28fbec1a4f7c27e95af55962e Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 12 Oct 2019 16:07:45 +0100 Subject: [PATCH 72/87] Ungate improved slice patterns diagnostics --- src/librustc_mir/hair/pattern/_match.rs | 17 ++++------------- src/test/ui/consts/const_let_refutable.stderr | 4 ++-- .../match-byte-array-patterns-2.stderr | 4 ++-- .../uninhabited-matches-feature-gated.stderr | 4 ++-- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 4212def0c5c2f..58d6faf904013 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1209,19 +1209,10 @@ impl<'tcx> Constructor<'tcx> { } VarLenSlice(prefix_len, _suffix_len) => match ty.kind { ty::Slice(ty) | ty::Array(ty, _) => { - if cx.tcx.features().slice_patterns { - let prefix = pats.by_ref().take(prefix_len as usize).collect(); - let suffix = pats.collect(); - let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; - PatKind::Slice { prefix, slice: Some(wild), suffix } - } else { - // We don't want to output a variable-length slice pattern if the - // slice_patterns feature is not enabled. - // The constructor covers infinitely many slice lengths, but for diagnostic - // purposes it is correct to return only some examples of non-covered - // patterns. So we just return the smallest length pattern here. - PatKind::Slice { prefix: pats.collect(), slice: None, suffix: vec![] } - } + let prefix = pats.by_ref().take(prefix_len as usize).collect(); + let suffix = pats.collect(); + let wild = Pat { ty, span: DUMMY_SP, kind: Box::new(PatKind::Wild) }; + PatKind::Slice { prefix, slice: Some(wild), suffix } } _ => bug!("bad slice pattern {:?} {:?}", self, ty), }, diff --git a/src/test/ui/consts/const_let_refutable.stderr b/src/test/ui/consts/const_let_refutable.stderr index 7f15f02d4d37b..9acb4ad9cbbe5 100644 --- a/src/test/ui/consts/const_let_refutable.stderr +++ b/src/test/ui/consts/const_let_refutable.stderr @@ -1,8 +1,8 @@ -error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _]` not covered +error[E0005]: refutable pattern in function argument: `&[]`, `&[_]` and `&[_, _, _, ..]` not covered --> $DIR/const_let_refutable.rs:3:16 | LL | const fn slice([a, b]: &[i32]) -> i32 { - | ^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _]` not covered + | ^^^^^^ patterns `&[]`, `&[_]` and `&[_, _, _, ..]` not covered error[E0723]: can only call other `const fn` within a `const fn`, but `const <&i32 as std::ops::Add>::add` is not stable as `const fn` --> $DIR/const_let_refutable.rs:4:5 diff --git a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr index 9938c9c284d1c..6e52072e3bfec 100644 --- a/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr +++ b/src/test/ui/pattern/usefulness/match-byte-array-patterns-2.stderr @@ -6,11 +6,11 @@ LL | match buf { | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[]` not covered +error[E0004]: non-exhaustive patterns: `&[..]` not covered --> $DIR/match-byte-array-patterns-2.rs:10:11 | LL | match buf { - | ^^^ pattern `&[]` not covered + | ^^^ pattern `&[..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index a49344e45cec6..7af6075262c6d 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -30,11 +30,11 @@ LL | let _ = match x {}; | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: `&[_]` not covered +error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered --> $DIR/uninhabited-matches-feature-gated.rs:21:19 | LL | let _ = match x { - | ^ pattern `&[_]` not covered + | ^ pattern `&[_, ..]` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms From a66dcd5b812c853c2e9c8fd7e18d1bb336811b93 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 12 Oct 2019 17:01:05 +0100 Subject: [PATCH 73/87] Simplify slice subtraction as suggested by arielb1 https://github.com/rust-lang/rust/pull/65160#discussion_r334172868 --- src/librustc_mir/hair/pattern/_match.rs | 102 ++++++++++-------------- 1 file changed, 40 insertions(+), 62 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 58d6faf904013..9cf794abbeef6 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -225,6 +225,7 @@ use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; +use rustc_data_structures::fx::FxHashSet; use rustc_index::vec::Idx; use super::{compare_const_vals, PatternFoldable, PatternFolder}; @@ -942,25 +943,22 @@ impl<'tcx> Constructor<'tcx> { // [0] => {} // [_, _, _] => {} // [1, 2, 3, 4, 5, 6, ..] => {} - // [_, _, _, _, _, _, _, _] => {} + // [_, _, _, _, _, _, _] => {} // [0, ..] => {} // } // ``` // We want to know which constructors are matched by the last pattern, but are not // matched by the first four ones. Since we only speak of constructors here, we - // only care about the length of the slices and not the subpatterns. + // only care about the length of the slices and not the particular subpatterns. // For that, we first notice that because of the third pattern, all constructors of // lengths 6 or more are covered already. `max_len` will be `Some(6)`. - // Then we'll look at constructors of lengths < 6 to see which are missing. We can - // ignore pattern 4 because it's longer than 6. We are left with patterns 1 and 2. - // The `length` vector will therefore contain `[Start, Boundary(1), Boundary(3), - // Boundary(6)]`. - // The resulting list of remaining constructors will be those strictly between - // those boundaries. Knowing that `self_len` is 1, we get `[FixedLenSlice(2), - // FixedLenSlice(4), FixedLenSlice(5)]`. - // Notice that `FixedLenSlice(0)` is not covered by any of the patterns here, but - // we don't care because we only want constructors that _are_ matched by the last - // pattern. + // Then we'll look at fixed-length constructors to see which are missing. The + // returned list of constructors will be those of lengths in 1..6 that are not + // present in the match. Lengths 1, 3 and 7 are matched already, so we get + // `[FixedLenSlice(2), FixedLenSlice(4), FixedLenSlice(5)]`. + // If we had removed the third pattern, we would have instead returned + // `[FixedLenSlice(2), FixedLenSlice(4), FixedLenSlice(5), FixedLenSlice(6), + // VarLenSlice(8, 0)]`. // Initially we cover all slice lengths starting from self_len. let self_len = self_prefix + self_suffix; @@ -975,6 +973,10 @@ impl<'tcx> Constructor<'tcx> { }) .min(); + // The remaining range of lengths is now either `self_len..` + // or `self_len..max_len`. We then remove from that range all the + // individual FixedLenSlice lengths in used_ctors. + // If max_len <= self_len there are no lengths remaining. if let Some(max_len) = max_len { if max_len <= self_len { @@ -982,63 +984,39 @@ impl<'tcx> Constructor<'tcx> { } } - // The remaining range of lengths is now either `self_len..` - // or `self_len..max_len`. We then remove from that range all the - // individual FixedLenSlice lengths in used_ctors. For that, - // we extract all those lengths that are in our remaining range and - // sort them. Every such length becomes a boundary between ranges - // of the lengths that will remain. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - enum Length { - Start, // `Start` will be sorted before `Boundary` - Boundary(u64), - } - use Length::*; - - let mut lengths: Vec<_> = used_ctors + // Extract fixed-size lengths + let used_fixed_lengths: FxHashSet = used_ctors .iter() - // Extract fixed-size lengths .filter_map(|c: &Constructor<'tcx>| match c { - FixedLenSlice(other_len) => Some(*other_len), + FixedLenSlice(len) => Some(*len), _ => None, }) - // Keep only those in the remaining range - .filter(|l| *l >= self_len) - .filter(|l| match max_len { - Some(max_len) => *l < max_len, - None => true, - }) - .chain(max_len) // Add max_len as the final boundary - .map(Boundary) - .chain(Some(Start)) // Add a special starting boundary - .collect(); - lengths.sort_unstable(); - lengths.dedup(); - - // For each adjacent pair of lengths, output the lengths in between. - let mut remaining_ctors: SmallVec<_> = lengths - .windows(2) - .flat_map(|window| match (window[0], window[1]) { - (Boundary(n), Boundary(m)) => (n + 1..m), - (Start, Boundary(m)) => (self_len..m), - _ => bug!(), - }) - .map(FixedLenSlice) .collect(); - // If there was a max_len, then we're done. Otherwise, we - // still need to include all lengths starting from the longest - // one until infinity, using VarLenSlice. - if max_len.is_none() { - let final_length = match lengths.last().unwrap() { - Start => self_len, - Boundary(n) => n + 1, - }; - // We know final_length >= self_len >= self_suffix so this can't underflow. - remaining_ctors.push(VarLenSlice(final_length - self_suffix, self_suffix)); + if let Some(max_len) = max_len { + (self_len..max_len) + .filter(|len| !used_fixed_lengths.contains(len)) + .map(FixedLenSlice) + .collect() + } else { + // Choose a length for which we know that all larger lengths remain in the + // output. + let min_free_length = used_fixed_lengths + .iter() + .map(|len| len + 1) + .chain(Some(self_len)) + .max() + .unwrap(); + + // We know min_free_length >= self_len >= self_suffix so this can't underflow. + let final_varlen = VarLenSlice(min_free_length - self_suffix, self_suffix); + + (self_len..min_free_length) + .filter(|len| !used_fixed_lengths.contains(len)) + .map(FixedLenSlice) + .chain(Some(final_varlen)) + .collect() } - - remaining_ctors } IntRange(range) => { let used_ranges = used_ctors.iter().flat_map(IntRange::from_ctor); From a2624f25bc34f303a4697a995cc2c50cac1dc1bd Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 12 Oct 2019 17:32:21 +0100 Subject: [PATCH 74/87] Rewrite constructor_intersects_pattern as a match --- src/librustc_mir/hair/pattern/_match.rs | 93 +++++++++++++------------ 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 9cf794abbeef6..ac436c96c0e53 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1940,52 +1940,55 @@ fn slice_pat_covered_by_const<'tcx>( // This has a single call site that can be hot #[inline(always)] fn constructor_intersects_pattern<'p, 'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, + cx: &MatchCheckCtxt<'_, 'tcx>, ctor: &Constructor<'tcx>, pat: &'p Pat<'tcx>, ) -> Option> { trace!("constructor_intersects_pattern {:#?}, {:#?}", ctor, pat); - if let Single = ctor { - Some(PatStack::default()) - } else if let IntRange(ctor) = ctor { - let pat = match *pat.kind { - PatKind::Constant { value } => IntRange::from_const(tcx, param_env, value)?, - PatKind::Range(PatRange { lo, hi, end }) => { - IntRange::from_range(tcx, param_env, lo, hi, &end)? - } - _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), - }; + match ctor { + Single => Some(PatStack::default()), + IntRange(ctor) => { + let pat = match *pat.kind { + PatKind::Constant { value } => IntRange::from_const(cx.tcx, cx.param_env, value)?, + PatKind::Range(PatRange { lo, hi, end }) => { + IntRange::from_range(cx.tcx, cx.param_env, lo, hi, &end)? + } + _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), + }; - ctor.intersection(tcx, &pat)?; - - // Constructor splitting should ensure that all intersections we encounter are actually - // inclusions. - let (pat_lo, pat_hi) = pat.range.into_inner(); - let (ctor_lo, ctor_hi) = ctor.range.clone().into_inner(); - assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); - - Some(PatStack::default()) - } else { - // Fallback for non-ranges and ranges that involve floating-point numbers, which are not - // conveniently handled by `IntRange`. For these cases, the constructor may not be a range - // so intersection actually devolves into being covered by the pattern. - let (pat_from, pat_to, pat_end) = match *pat.kind { - PatKind::Constant { value } => (value, value, RangeEnd::Included), - PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end), - _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), - }; - let (ctor_from, ctor_to, ctor_end) = match *ctor { - ConstantValue(value) => (value, value, RangeEnd::Included), - ConstantRange(from, to, range_end) => (from, to, range_end), - _ => bug!("`constructor_intersects_pattern` called with {:?}", ctor), - }; - let order_to = compare_const_vals(tcx, ctor_to, pat_to, param_env, pat_from.ty)?; - let order_from = compare_const_vals(tcx, ctor_from, pat_from, param_env, pat_from.ty)?; - let included = (order_from != Ordering::Less) - && ((order_to == Ordering::Less) - || (pat_end == ctor_end && order_to == Ordering::Equal)); - if included { Some(PatStack::default()) } else { None } + ctor.intersection(cx.tcx, &pat)?; + + // Constructor splitting should ensure that all intersections we encounter are actually + // inclusions. + let (pat_lo, pat_hi) = pat.range.into_inner(); + let (ctor_lo, ctor_hi) = ctor.range.clone().into_inner(); + assert!(pat_lo <= ctor_lo && ctor_hi <= pat_hi); + + Some(PatStack::default()) + } + ConstantValue(..) | ConstantRange(..) => { + // Fallback for non-ranges and ranges that involve floating-point numbers, which are + // not conveniently handled by `IntRange`. For these cases, the constructor may not be + // a range so intersection actually devolves into being covered by the pattern. + let (pat_from, pat_to, pat_end) = match *pat.kind { + PatKind::Constant { value } => (value, value, RangeEnd::Included), + PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end), + _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), + }; + let (ctor_from, ctor_to, ctor_end) = match *ctor { + ConstantValue(value) => (value, value, RangeEnd::Included), + ConstantRange(from, to, range_end) => (from, to, range_end), + _ => bug!(), + }; + let order_to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, pat_from.ty)?; + let order_from = + compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, pat_from.ty)?; + let included = (order_from != Ordering::Less) + && ((order_to == Ordering::Less) + || (pat_end == ctor_end && order_to == Ordering::Equal)); + if included { Some(PatStack::default()) } else { None } + } + _ => bug!("`constructor_intersects_pattern` called with {:?}", ctor), } } @@ -2122,9 +2125,11 @@ fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( // 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. - constructor_intersects_pattern(cx.tcx, cx.param_env, constructor, pat) - .into_iter() - .collect() + if let Some(ps) = constructor_intersects_pattern(cx, constructor, pat) { + smallvec![ps] + } else { + smallvec![] + } } PatKind::Array { ref prefix, ref slice, ref suffix } From 79efdb45384fd4f3d8152c380ddd4435e19cdf60 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 12 Oct 2019 18:34:39 +0100 Subject: [PATCH 75/87] Remove unnecessary Const conversion --- src/librustc_mir/hair/pattern/_match.rs | 66 ++++++++++++------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index ac436c96c0e53..34da86baa5470 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1387,63 +1387,48 @@ fn all_constructors<'a, 'tcx>( .map(|v| Variant(v.def_id)) .collect(), ty::Char => { - let param_env = ty::ParamEnv::empty().and(cx.tcx.types.char); - let to_const = |x| ty::Const::from_bits(cx.tcx, x as u128, param_env); + let to_const = |x| x; vec![ // The valid Unicode Scalar Value ranges. IntRange( IntRange::from_range( cx.tcx, - cx.param_env, - to_const('\u{0000}'), - to_const('\u{D7FF}'), - &RangeEnd::Included, + cx.tcx.types.char, + to_const('\u{0000}' as u128), + to_const('\u{D7FF}' as u128), + RangeEnd::Included, ) .unwrap(), ), IntRange( IntRange::from_range( cx.tcx, - cx.param_env, - to_const('\u{E000}'), - to_const('\u{10FFFF}'), - &RangeEnd::Included, + cx.tcx.types.char, + to_const('\u{E000}' as u128), + to_const('\u{10FFFF}' as u128), + RangeEnd::Included, ) .unwrap(), ), ] } ty::Int(ity) => { - let param_env = ty::ParamEnv::empty().and(ty); - let to_const = |x| ty::Const::from_bits(cx.tcx, x, param_env); + let to_const = |x| x; let bits = Integer::from_attr(&cx.tcx, SignedInt(ity)).size().bits() as u128; let min = 1u128 << (bits - 1); let max = min - 1; vec![IntRange( - IntRange::from_range( - cx.tcx, - cx.param_env, - to_const(min), - to_const(max), - &RangeEnd::Included, - ) - .unwrap(), + IntRange::from_range(cx.tcx, ty, to_const(min), to_const(max), RangeEnd::Included) + .unwrap(), )] } ty::Uint(uty) => { - let param_env = ty::ParamEnv::empty().and(ty); - let to_const = |x| ty::Const::from_bits(cx.tcx, x, param_env); + let to_const = |x| x; let size = Integer::from_attr(&cx.tcx, UnsignedInt(uty)).size(); let max = truncate(u128::max_value(), size); vec![IntRange( - IntRange::from_range( - cx.tcx, - cx.param_env, - to_const(0), - to_const(max), - &RangeEnd::Included, - ) - .unwrap(), + IntRange::from_range(cx.tcx, ty, to_const(0), to_const(max), RangeEnd::Included) + .unwrap(), )] } _ => { @@ -1535,7 +1520,7 @@ impl<'tcx> IntRange<'tcx> { } #[inline] - fn from_range( + fn from_const_range( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, lo: &Const<'tcx>, @@ -1545,16 +1530,27 @@ impl<'tcx> IntRange<'tcx> { let ty = lo.ty; let lo = lo.eval_bits(tcx, param_env, lo.ty); let hi = hi.eval_bits(tcx, param_env, hi.ty); + Self::from_range(tcx, ty, lo, hi, *end) + } + + #[inline] + fn from_range( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + lo: u128, + hi: u128, + end: RangeEnd, + ) -> Option> { if Self::is_integral(ty) { // Perform a shift if the underlying types are signed, // which makes the interval arithmetic simpler. let bias = IntRange::signed_bias(tcx, ty); let (lo, hi) = (lo ^ bias, hi ^ bias); // Make sure the interval is well-formed. - if lo > hi || lo == hi && *end == RangeEnd::Excluded { + if lo > hi || lo == hi && end == RangeEnd::Excluded { None } else { - let offset = (*end == RangeEnd::Excluded) as u128; + let offset = (end == RangeEnd::Excluded) as u128; Some(IntRange { range: lo..=(hi - offset), ty }) } } else { @@ -1851,7 +1847,7 @@ fn pat_constructors<'tcx>( } } PatKind::Range(PatRange { lo, hi, end }) => { - if let Some(range) = IntRange::from_range(tcx, param_env, &lo, &hi, &end) { + if let Some(range) = IntRange::from_const_range(tcx, param_env, &lo, &hi, &end) { smallvec![IntRange(range)] } else { smallvec![ConstantRange(lo, hi, end)] @@ -1951,7 +1947,7 @@ fn constructor_intersects_pattern<'p, 'tcx>( let pat = match *pat.kind { PatKind::Constant { value } => IntRange::from_const(cx.tcx, cx.param_env, value)?, PatKind::Range(PatRange { lo, hi, end }) => { - IntRange::from_range(cx.tcx, cx.param_env, lo, hi, &end)? + IntRange::from_const_range(cx.tcx, cx.param_env, lo, hi, &end)? } _ => bug!("`constructor_intersects_pattern` called with {:?}", pat), }; From 1f6a5a5744c2c563a624ba4afa46a18fdeef1a8a Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Sat, 12 Oct 2019 18:55:13 +0100 Subject: [PATCH 76/87] Clarify why the naive algorithm may not terminate As suggested by arielb1 https://github.com/rust-lang/rust/pull/65160#pullrequestreview-300906588 --- 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 34da86baa5470..e9c89692a7559 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -114,9 +114,10 @@ /// the formula above makes sense. /// /// This algorithm however has a lot of practical issues. Most importantly, it may not terminate -/// in the presence of recursive types, since we always unpack all constructors as much -/// as possible. And it would be stupidly slow anyways for types with a lot of constructors, -/// like `u64` of `&[bool]`. We therefore present a modified version after the example. +/// for some types with infinitely many inhabitants, because when it encounters a wildcard it will +/// try all the values of the type. And it would be stupidly slow anyways for types with a lot of +/// constructors, like `u64` of `&[bool]`. We therefore present a modified version after the +/// example. /// /// /// # Example run of the algorithm From 455095a82b26907e99a5a847e7d657b69655a474 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 14 Oct 2019 17:05:48 +0100 Subject: [PATCH 77/87] Remove unneeded lifetime extensions --- src/librustc_mir/hair/pattern/_match.rs | 39 ++++++++++--------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index e9c89692a7559..d2affe3d5488e 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -363,10 +363,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { self.0.len() } - fn head<'a, 'p2>(&'a self) -> &'p2 Pat<'tcx> - where - 'p: 'p2, - { + fn head<'a>(&'a self) -> &'p Pat<'tcx> { self.0[0] } fn iter(&self) -> impl Iterator> { @@ -374,15 +371,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize<'a, 'p2>( + fn specialize<'a, 'q>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], - ) -> SmallVec<[PatStack<'p2, 'tcx>; 1]> + ctor_wild_subpatterns: &[&'q Pat<'tcx>], + ) -> SmallVec<[PatStack<'q, 'tcx>; 1]> where - 'a: 'p2, - 'p: 'p2, + 'a: 'q, + 'p: 'q, { let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); let result = new_heads @@ -426,29 +423,25 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { /// Iterate over the first component of each row // Can't return impl Iterator because of hidden lifetime capture. - fn heads<'a, 'p2>( + fn heads<'a>( &'a self, ) -> iter::Map< std::slice::Iter<'a, PatStack<'p, 'tcx>>, - impl FnMut(&'a PatStack<'p, 'tcx>) -> &'p2 Pat<'tcx>, - > - where - 'p: 'p2, - 'a: 'p2, - { + impl FnMut(&'a PatStack<'p, 'tcx>) -> &'p Pat<'tcx>, + > { self.0.iter().map(|r| r.head()) } /// This computes `S(constructor, self)`. See top of the file for explanations. - fn specialize<'a, 'p2>( + fn specialize<'a, 'q>( &self, cx: &MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, - ctor_wild_subpatterns: &[&'p2 Pat<'tcx>], - ) -> Matrix<'p2, 'tcx> + ctor_wild_subpatterns: &[&'q Pat<'tcx>], + ) -> Matrix<'q, 'tcx> where - 'a: 'p2, - 'p: 'p2, + 'a: 'q, + 'p: 'q, { Matrix( self.0 @@ -2013,9 +2006,9 @@ fn patterns_for_variant<'p, 'tcx>( /// /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing /// fields filled with wild patterns. -fn specialize_one_pattern<'p, 'a: 'p, 'p2: 'p, 'tcx>( +fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( cx: &MatchCheckCtxt<'a, 'tcx>, - mut pat: &'p2 Pat<'tcx>, + mut pat: &'q Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> SmallVec<[PatStack<'p, 'tcx>; 1]> { From d379a433d35fe9340313cc745ee41e577825bdcc Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Mon, 14 Oct 2019 17:09:37 +0100 Subject: [PATCH 78/87] Use special `Captures` trait to solve issues with impl Iterator --- src/librustc_mir/hair/pattern/_match.rs | 26 ++++--------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index d2affe3d5488e..b39470ccc7e2a 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::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}; @@ -250,7 +251,7 @@ use smallvec::{smallvec, SmallVec}; use std::cmp::{self, max, min, Ordering}; use std::convert::TryInto; use std::fmt; -use std::iter::{self, FromIterator, IntoIterator}; +use std::iter::{FromIterator, IntoIterator}; use std::ops::RangeInclusive; use std::u128; @@ -422,13 +423,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } /// Iterate over the first component of each row - // Can't return impl Iterator because of hidden lifetime capture. - fn heads<'a>( - &'a self, - ) -> iter::Map< - std::slice::Iter<'a, PatStack<'p, 'tcx>>, - impl FnMut(&'a PatStack<'p, 'tcx>) -> &'p Pat<'tcx>, - > { + fn heads<'a>(&'a self) -> impl Iterator> + Captures<'p> { self.0.iter().map(|r| r.head()) } @@ -1642,12 +1637,6 @@ struct MissingConstructors<'tcx> { used_ctors: Vec>, } -type MissingConstructorsIter<'a, 'tcx, F> = std::iter::FlatMap< - std::slice::Iter<'a, Constructor<'tcx>>, - SmallVec<[Constructor<'tcx>; 1]>, - F, ->; - impl<'tcx> MissingConstructors<'tcx> { fn new( tcx: TyCtxt<'tcx>, @@ -1667,14 +1656,7 @@ impl<'tcx> MissingConstructors<'tcx> { } /// Iterate over all_ctors \ used_ctors - // Can't use impl Iterator because of lifetime shenanigans - fn iter<'a>( - &'a self, - ) -> MissingConstructorsIter< - 'a, - 'tcx, - impl FnMut(&'a Constructor<'tcx>) -> SmallVec<[Constructor<'tcx>; 1]>, - > { + fn iter<'a>(&'a self) -> impl Iterator> + Captures<'a> { self.all_ctors.iter().flat_map(move |req_ctor| { req_ctor.clone().subtract_meta_constructor(self.tcx, self.param_env, &self.used_ctors) }) From 1de16831141b78716e0741e7b6ddd344c1af3b1b Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 16 Oct 2019 20:08:11 +0100 Subject: [PATCH 79/87] `pat_constructors` doesn't need a `ty` parameter --- src/librustc_mir/hair/pattern/_match.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index b39470ccc7e2a..1a197cceb4345 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -1747,7 +1747,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", ty, v.head()); - let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head(), ty); + let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head()); if cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(ty) @@ -1760,7 +1760,7 @@ pub fn is_useful<'p, 'a, 'tcx>( let matrix_head_ctors: Vec> = matrix .heads() - .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p, ty)) + .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p)) .filter(|ctor| !ctor.is_wildcard()) .collect(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); @@ -1804,11 +1804,10 @@ fn pat_constructors<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, - ty: Ty<'tcx>, ) -> SmallVec<[Constructor<'tcx>; 1]> { match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { - pat_constructors(tcx, param_env, subpattern, ty) + pat_constructors(tcx, param_env, subpattern) } PatKind::Binding { .. } | PatKind::Wild => smallvec![Wildcard], PatKind::Leaf { .. } | PatKind::Deref { .. } => smallvec![Single], @@ -1829,9 +1828,9 @@ fn pat_constructors<'tcx>( smallvec![ConstantRange(lo, hi, end)] } } - PatKind::Array { .. } => match ty.kind { + PatKind::Array { .. } => match pat.ty.kind { ty::Array(_, length) => smallvec![FixedLenSlice(length.eval_usize(tcx, param_env))], - _ => span_bug!(pat.span, "bad ty {:?} for array pattern", ty), + _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let prefix = prefix.len() as u64; From 3aa5fb33ea89c173349228195e99c7804d3c2187 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 17 Oct 2019 14:26:48 +0100 Subject: [PATCH 80/87] Extract head constructors construction --- src/librustc_mir/hair/pattern/_match.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 1a197cceb4345..92a16ec96819f 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -367,6 +367,11 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { fn head<'a>(&'a self) -> &'p Pat<'tcx> { self.0[0] } + + fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { + pat_constructors(cx.tcx, cx.param_env, self.head()) + } + fn iter(&self) -> impl Iterator> { self.0.iter().map(|p| *p) } @@ -427,6 +432,10 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.0.iter().map(|r| r.head()) } + fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Vec> { + self.0.iter().flat_map(|r| r.head_ctors(cx)).filter(|ctor| !ctor.is_wildcard()).collect() + } + /// This computes `S(constructor, self)`. See top of the file for explanations. fn specialize<'a, 'q>( &self, @@ -1747,7 +1756,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", ty, v.head()); - let v_constructors = pat_constructors(cx.tcx, cx.param_env, v.head()); + let v_constructors = v.head_ctors(cx); if cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(ty) @@ -1758,11 +1767,7 @@ pub fn is_useful<'p, 'a, 'tcx>( return Useful; } - let matrix_head_ctors: Vec> = matrix - .heads() - .flat_map(|p| pat_constructors(cx.tcx, cx.param_env, p)) - .filter(|ctor| !ctor.is_wildcard()) - .collect(); + let matrix_head_ctors = matrix.head_ctors(cx); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); v_constructors From c956c65604dd6084af89e1503e4b4cc1a56f2af4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 17 Oct 2019 14:31:10 +0100 Subject: [PATCH 81/87] Name the field of PatStack --- src/librustc_mir/hair/pattern/_match.rs | 26 +++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 92a16ec96819f..a098141f66844 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -337,35 +337,37 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { /// 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]>); +pub struct PatStack<'p, 'tcx> { + patterns: SmallVec<[&'p Pat<'tcx>; 2]>, +} impl<'p, 'tcx> PatStack<'p, 'tcx> { pub fn from_pattern(pat: &'p Pat<'tcx>) -> Self { - PatStack(smallvec![pat]) + PatStack::from_vec(smallvec![pat]) } fn empty() -> Self { - PatStack(smallvec![]) + PatStack::from_vec(smallvec![]) } fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { - PatStack(vec) + PatStack { patterns: vec } } fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { - PatStack(SmallVec::from_slice(s)) + PatStack::from_vec(SmallVec::from_slice(s)) } fn is_empty(&self) -> bool { - self.0.is_empty() + self.patterns.is_empty() } fn len(&self) -> usize { - self.0.len() + self.patterns.len() } fn head<'a>(&'a self) -> &'p Pat<'tcx> { - self.0[0] + self.patterns[0] } fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { @@ -373,7 +375,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } fn iter(&self) -> impl Iterator> { - self.0.iter().map(|p| *p) + self.patterns.iter().map(|p| *p) } /// This computes `S(constructor, self)`. See top of the file for explanations. @@ -391,7 +393,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { let result = new_heads .into_iter() .map(|mut new_head| { - new_head.0.extend_from_slice(&self.0[1..]); + new_head.patterns.extend_from_slice(&self.patterns[1..]); new_head }) .collect(); @@ -402,7 +404,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { fn default() -> Self { - PatStack(smallvec![]) + PatStack::empty() } } @@ -411,7 +413,7 @@ impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { where T: IntoIterator>, { - PatStack(iter.into_iter().collect()) + PatStack::from_vec(iter.into_iter().collect()) } } From 368c71b74eea938990e6305c008ec7ceee4648f2 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 17 Oct 2019 15:10:04 +0100 Subject: [PATCH 82/87] Cache `pat_constructors` invocations --- src/librustc_mir/hair/pattern/_match.rs | 26 ++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index a098141f66844..7463f06e71588 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -248,6 +248,7 @@ use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; use smallvec::{smallvec, SmallVec}; +use std::cell::RefCell; use std::cmp::{self, max, min, Ordering}; use std::convert::TryInto; use std::fmt; @@ -339,6 +340,9 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { #[derive(Debug, Clone)] pub struct PatStack<'p, 'tcx> { patterns: SmallVec<[&'p Pat<'tcx>; 2]>, + // This caches the invocation of `pat_constructors` on the head of the stack. We avoid mutating + // `self` to be sure we don't keep an invalid cache around. + head_ctors_cache: RefCell; 1]>>>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { @@ -351,7 +355,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { - PatStack { patterns: vec } + PatStack { patterns: vec, head_ctors_cache: RefCell::new(None) } } fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { @@ -371,7 +375,18 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { - pat_constructors(cx.tcx, cx.param_env, self.head()) + let new_ctors = pat_constructors(cx.tcx, cx.param_env, self.head()); + let borrow = self.head_ctors_cache.borrow(); + match *borrow { + Some(ref cached_ctors) => { + assert_eq!(cached_ctors, &new_ctors); + } + None => { + drop(borrow); + *self.head_ctors_cache.borrow_mut() = Some(new_ctors.clone()); + } + } + new_ctors } fn iter(&self) -> impl Iterator> { @@ -392,9 +407,10 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); let result = new_heads .into_iter() - .map(|mut new_head| { - new_head.patterns.extend_from_slice(&self.patterns[1..]); - new_head + .map(|new_head| { + let mut pats = new_head.patterns; + pats.extend_from_slice(&self.patterns[1..]); + PatStack::from_vec(pats) }) .collect(); debug!("specialize({:#?}, {:#?}) = {:#?}", self, constructor, result); From 6df18152cf6fedac6065648157c1e213fdc20bde Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 17 Oct 2019 15:21:57 +0100 Subject: [PATCH 83/87] Compute constructors on PatStack construction All the constructed PatStacks end up having `head_ctors` called on them, so this does not unnecessarily precompute anything. --- src/librustc_mir/hair/pattern/_match.rs | 69 +++++++++----------- src/librustc_mir/hair/pattern/check_match.rs | 8 +-- 2 files changed, 34 insertions(+), 43 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 7463f06e71588..069476c5c6bd4 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -248,7 +248,6 @@ use syntax_pos::{Span, DUMMY_SP}; use arena::TypedArena; use smallvec::{smallvec, SmallVec}; -use std::cell::RefCell; use std::cmp::{self, max, min, Ordering}; use std::convert::TryInto; use std::fmt; @@ -341,25 +340,30 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { pub struct PatStack<'p, 'tcx> { patterns: SmallVec<[&'p Pat<'tcx>; 2]>, // This caches the invocation of `pat_constructors` on the head of the stack. We avoid mutating - // `self` to be sure we don't keep an invalid cache around. - head_ctors_cache: RefCell; 1]>>>, + // `self` to be sure we don't keep an invalid cache around. Must be non-empty unless `patterns` + // is empty. + head_ctors_cache: SmallVec<[Constructor<'tcx>; 1]>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { - pub fn from_pattern(pat: &'p Pat<'tcx>) -> Self { - PatStack::from_vec(smallvec![pat]) + pub fn from_pattern(cx: &MatchCheckCtxt<'_, 'tcx>, pat: &'p Pat<'tcx>) -> Self { + PatStack::from_vec(cx, smallvec![pat]) } fn empty() -> Self { - PatStack::from_vec(smallvec![]) + PatStack { patterns: smallvec![], head_ctors_cache: smallvec![] } } - fn from_vec(vec: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { - PatStack { patterns: vec, head_ctors_cache: RefCell::new(None) } + fn from_vec(cx: &MatchCheckCtxt<'_, 'tcx>, patterns: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { + if patterns.is_empty() { + return PatStack::empty(); + } + let head_ctors_cache = pat_constructors(cx.tcx, cx.param_env, patterns[0]); + PatStack { patterns, head_ctors_cache } } - fn from_slice(s: &[&'p Pat<'tcx>]) -> Self { - PatStack::from_vec(SmallVec::from_slice(s)) + fn from_slice(cx: &MatchCheckCtxt<'_, 'tcx>, s: &[&'p Pat<'tcx>]) -> Self { + PatStack::from_vec(cx, SmallVec::from_slice(s)) } fn is_empty(&self) -> bool { @@ -376,16 +380,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { let new_ctors = pat_constructors(cx.tcx, cx.param_env, self.head()); - let borrow = self.head_ctors_cache.borrow(); - match *borrow { - Some(ref cached_ctors) => { - assert_eq!(cached_ctors, &new_ctors); - } - None => { - drop(borrow); - *self.head_ctors_cache.borrow_mut() = Some(new_ctors.clone()); - } - } + assert_eq!(self.head_ctors_cache, new_ctors); new_ctors } @@ -410,7 +405,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { .map(|new_head| { let mut pats = new_head.patterns; pats.extend_from_slice(&self.patterns[1..]); - PatStack::from_vec(pats) + PatStack::from_vec(cx, pats) }) .collect(); debug!("specialize({:#?}, {:#?}) = {:#?}", self, constructor, result); @@ -424,15 +419,6 @@ impl<'p, 'tcx> Default for PatStack<'p, 'tcx> { } } -impl<'p, 'tcx> FromIterator<&'p Pat<'tcx>> for PatStack<'p, 'tcx> { - fn from_iter(iter: T) -> Self - where - T: IntoIterator>, - { - PatStack::from_vec(iter.into_iter().collect()) - } -} - /// A 2D matrix. pub struct Matrix<'p, 'tcx>(Vec>); @@ -1987,6 +1973,7 @@ fn constructor_intersects_pattern<'p, 'tcx>( } fn patterns_for_variant<'p, 'tcx>( + cx: &MatchCheckCtxt<'_, 'tcx>, subpatterns: &'p [FieldPat<'tcx>], ctor_wild_subpatterns: &[&'p Pat<'tcx>], ) -> PatStack<'p, 'tcx> { @@ -2000,7 +1987,7 @@ fn patterns_for_variant<'p, 'tcx>( "patterns_for_variant({:#?}, {:#?}) = {:#?}", subpatterns, ctor_wild_subpatterns, result ); - PatStack::from_vec(result) + PatStack::from_vec(cx, result) } /// This is the main specialization step. It expands the pattern into `arity` patterns based on the @@ -2037,23 +2024,23 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( PatKind::AscribeUserType { .. } => unreachable!(), // Handled above PatKind::Binding { .. } | PatKind::Wild => { - smallvec![PatStack::from_slice(ctor_wild_subpatterns)] + smallvec![PatStack::from_slice(cx, ctor_wild_subpatterns)] } PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; if Variant(variant.def_id) == *constructor { - smallvec![patterns_for_variant(subpatterns, ctor_wild_subpatterns)] + smallvec![patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns)] } else { smallvec![] } } PatKind::Leaf { ref subpatterns } => { - smallvec![patterns_for_variant(subpatterns, ctor_wild_subpatterns)] + smallvec![patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns)] } - PatKind::Deref { ref subpattern } => smallvec![PatStack::from_pattern(subpattern)], + PatKind::Deref { ref subpattern } => smallvec![PatStack::from_pattern(cx, subpattern)], PatKind::Constant { value } if constructor.is_slice() => { // We extract an `Option` for the pointer because slices of zero @@ -2098,7 +2085,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( return smallvec![]; }; let ptr = Pointer::new(AllocId(0), offset); - let stack: Option> = (0..n) + let stack: Option> = (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()?; @@ -2109,7 +2096,10 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( Some(&*cx.pattern_arena.alloc(pattern)) }) .collect(); - stack.into_iter().collect() + match stack { + Some(v) => smallvec![PatStack::from_vec(cx, v)], + None => smallvec![], + } } else { smallvec![] } @@ -2132,7 +2122,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { - smallvec![ + smallvec![PatStack::from_vec( + cx, prefix .iter() .chain( @@ -2144,7 +2135,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( .chain(suffix.iter()), ) .collect(), - ] + )] } else { smallvec![] } diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 048f9fa11e1c1..784e6c59cf9da 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -246,7 +246,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { .iter() .filter(|&&(_, guard)| guard.is_none()) .flat_map(|arm| &arm.0) - .map(|pat| PatStack::from_pattern(pat.0)) + .map(|pat| PatStack::from_pattern(cx, pat.0)) .collect(); let scrut_ty = self.tables.node_type(scrut.hir_id); check_exhaustive(cx, scrut_ty, scrut.span, &matrix); @@ -262,7 +262,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { let pattern = patcx.lower_pattern(pat); let pattern_ty = pattern.ty; let pats: Matrix<'_, '_> = - vec![PatStack::from_pattern(expand_pattern(cx, pattern))].into_iter().collect(); + vec![PatStack::from_pattern(cx, expand_pattern(cx, pattern))].into_iter().collect(); let witnesses = match check_not_useful(cx, pattern_ty, &pats) { Ok(_) => return, @@ -406,7 +406,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 = PatStack::from_pattern(pat); + let v = PatStack::from_pattern(cx, pat); match is_useful(cx, &seen, &v, LeaveOutWitness) { NotUseful => { @@ -487,7 +487,7 @@ fn check_not_useful( matrix: &Matrix<'_, 'tcx>, ) -> Result<(), Vec>> { let wild_pattern = super::Pat { ty, span: DUMMY_SP, kind: box PatKind::Wild }; - match is_useful(cx, matrix, &PatStack::from_pattern(&wild_pattern), ConstructWitness) { + match is_useful(cx, matrix, &PatStack::from_pattern(cx, &wild_pattern), ConstructWitness) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { vec![wild_pattern] From ed3ea672e8c089168161046043d11475f72103cf Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 17 Oct 2019 15:44:47 +0100 Subject: [PATCH 84/87] Restore performance Stop checking that cached constructors are valid. Avoid cloning constructors when not necessary. --- src/librustc_mir/hair/pattern/_match.rs | 51 +++++++++++++------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 069476c5c6bd4..3845928c7daf8 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -334,15 +334,15 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { } } -/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]` -/// works well. +/// A row of a matrix. #[derive(Debug, Clone)] pub struct PatStack<'p, 'tcx> { + // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well. patterns: SmallVec<[&'p Pat<'tcx>; 2]>, // This caches the invocation of `pat_constructors` on the head of the stack. We avoid mutating // `self` to be sure we don't keep an invalid cache around. Must be non-empty unless `patterns` // is empty. - head_ctors_cache: SmallVec<[Constructor<'tcx>; 1]>, + head_ctors: SmallVec<[Constructor<'tcx>; 1]>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { @@ -351,15 +351,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } fn empty() -> Self { - PatStack { patterns: smallvec![], head_ctors_cache: smallvec![] } + PatStack { patterns: smallvec![], head_ctors: smallvec![] } } fn from_vec(cx: &MatchCheckCtxt<'_, 'tcx>, patterns: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { if patterns.is_empty() { return PatStack::empty(); } - let head_ctors_cache = pat_constructors(cx.tcx, cx.param_env, patterns[0]); - PatStack { patterns, head_ctors_cache } + let head_ctors = pat_constructors(cx.tcx, cx.param_env, patterns[0]); + PatStack { patterns, head_ctors } } fn from_slice(cx: &MatchCheckCtxt<'_, 'tcx>, s: &[&'p Pat<'tcx>]) -> Self { @@ -378,10 +378,8 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { self.patterns[0] } - fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> SmallVec<[Constructor<'tcx>; 1]> { - let new_ctors = pat_constructors(cx.tcx, cx.param_env, self.head()); - assert_eq!(self.head_ctors_cache, new_ctors); - new_ctors + fn head_ctors(&self) -> &SmallVec<[Constructor<'tcx>; 1]> { + &self.head_ctors } fn iter(&self) -> impl Iterator> { @@ -436,8 +434,8 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { self.0.iter().map(|r| r.head()) } - fn head_ctors(&self, cx: &MatchCheckCtxt<'_, 'tcx>) -> Vec> { - self.0.iter().flat_map(|r| r.head_ctors(cx)).filter(|ctor| !ctor.is_wildcard()).collect() + fn head_ctors(&self) -> Vec<&Constructor<'tcx>> { + self.0.iter().flat_map(|r| r.head_ctors()).filter(|ctor| !ctor.is_wildcard()).collect() } /// This computes `S(constructor, self)`. See top of the file for explanations. @@ -641,18 +639,18 @@ impl<'tcx> Constructor<'tcx> { /// for the given matrix. See description of the algorithm for details. /// Note: We can rely on this returning an empty list if the type is (visibly) uninhabited. fn split_meta_constructor( - self, + &self, cx: &MatchCheckCtxt<'_, 'tcx>, ty: Ty<'tcx>, - head_ctors: &Vec>, + head_ctors: &Vec<&Constructor<'tcx>>, ) -> SmallVec<[Constructor<'tcx>; 1]> { debug!("split_meta_constructor {:?}", self); assert!(!head_ctors.iter().any(|c| c.is_wildcard())); - match self { + match *self { // Any base constructor can be used unchanged. - Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self], - IntRange(ctor_range) if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => { + Single | Variant(_) | ConstantValue(_) | FixedLenSlice(_) => smallvec![self.clone()], + IntRange(ref ctor_range) if IntRange::should_treat_range_exhaustively(cx.tcx, ty) => { // Splitting up a range naïvely would mean creating a separate constructor for // every single value in the range, which is clearly impractical. We therefore want // to keep together subranges for which the specialisation will be identical across @@ -708,6 +706,7 @@ impl<'tcx> Constructor<'tcx> { // class lies between 2 borders. let row_borders = head_ctors .iter() + .map(|c| *c) .flat_map(IntRange::from_ctor) .flat_map(|range| ctor_range.intersection(cx.tcx, &range)) .flat_map(|range| range_borders(range)); @@ -736,7 +735,7 @@ impl<'tcx> Constructor<'tcx> { .collect() } // When not treated exhaustively, don't split ranges. - ConstantRange(..) | IntRange(..) => smallvec![self], + ConstantRange(..) | IntRange(..) => smallvec![self.clone()], VarLenSlice(self_prefix, self_suffix) => { // A variable-length slice pattern is matched by an infinite collection of // fixed-length array patterns. However it turns out that for each finite set of @@ -806,7 +805,7 @@ impl<'tcx> Constructor<'tcx> { let mut max_fixed_len = 0; for ctor in head_ctors { - match *ctor { + match **ctor { ConstantValue(value) => { // Extract the length of an array/slice from a constant match (value.val, &value.ty.kind) { @@ -868,8 +867,12 @@ impl<'tcx> Constructor<'tcx> { // Therefore, if there is some pattern that is unmatched by `matrix`, // it will still be unmatched if the first constructor is replaced by // any of the constructors in `missing_ctors` - let missing_ctors = - MissingConstructors::new(cx.tcx, cx.param_env, all_ctors, head_ctors.clone()); + let missing_ctors = MissingConstructors::new( + cx.tcx, + cx.param_env, + all_ctors, + head_ctors.iter().map(|c| (**c).clone()).collect(), + ); debug!( "missing_ctors.is_empty()={:#?} is_non_exhaustive={:#?}", missing_ctors.is_empty(), @@ -1760,7 +1763,7 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", ty, v.head()); - let v_constructors = v.head_ctors(cx); + let v_constructors = v.head_ctors(); if cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(ty) @@ -1771,11 +1774,11 @@ pub fn is_useful<'p, 'a, 'tcx>( return Useful; } - let matrix_head_ctors = matrix.head_ctors(cx); + let matrix_head_ctors = matrix.head_ctors(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); v_constructors - .into_iter() + .iter() .flat_map(|ctor| ctor.split_meta_constructor(cx, ty, &matrix_head_ctors)) .map(|c| is_useful_specialized(cx, matrix, v, c, ty, witness_preference)) .find(|result| result.is_useful()) From 9c3682f1284ff6dad5161c1cd1871fcfc6f9d2e6 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Thu, 17 Oct 2019 16:19:30 +0100 Subject: [PATCH 85/87] Don't rebuild the same matrix twice --- src/librustc_mir/hair/pattern/check_match.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 784e6c59cf9da..0f4c039b3fb48 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -175,7 +175,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { } // Fourth, check for unreachable arms. - check_arms(cx, &inlined_arms, source); + let matrix = check_arms(cx, &inlined_arms, source); // Then, if the match has no arms, check whether the scrutinee // is uninhabited. @@ -242,12 +242,6 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { return; } - let matrix: Matrix<'_, '_> = inlined_arms - .iter() - .filter(|&&(_, guard)| guard.is_none()) - .flat_map(|arm| &arm.0) - .map(|pat| PatStack::from_pattern(cx, pat.0)) - .collect(); let scrut_ty = self.tables.node_type(scrut.hir_id); check_exhaustive(cx, scrut_ty, scrut.span, &matrix); }) @@ -397,11 +391,11 @@ fn pat_is_catchall(pat: &Pat) -> bool { } // Check for unreachable patterns -fn check_arms<'tcx>( +fn check_arms<'p, 'tcx>( cx: &mut MatchCheckCtxt<'_, 'tcx>, - arms: &[(Vec<(&super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], + arms: &[(Vec<(&'p super::Pat<'tcx>, &hir::Pat)>, Option<&hir::Expr>)], source: hir::MatchSource, -) { +) -> Matrix<'p, 'tcx> { let mut seen = Matrix::empty(); let mut catchall = None; for (arm_index, &(ref pats, guard)) in arms.iter().enumerate() { @@ -479,6 +473,7 @@ fn check_arms<'tcx>( } } } + seen } fn check_not_useful( From b052036296f0a35aa8cec4556d7c1fd5caee1f56 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 18 Oct 2019 12:28:12 +0100 Subject: [PATCH 86/87] Don't use SmallVec when we could use Option This reverts some of the or-patterns future-proofing, for the sake of performance. --- src/librustc_mir/hair/pattern/_match.rs | 116 +++++++++++------------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 3845928c7daf8..eaf471454d285 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -342,7 +342,7 @@ pub struct PatStack<'p, 'tcx> { // This caches the invocation of `pat_constructors` on the head of the stack. We avoid mutating // `self` to be sure we don't keep an invalid cache around. Must be non-empty unless `patterns` // is empty. - head_ctors: SmallVec<[Constructor<'tcx>; 1]>, + head_ctor: Option>, } impl<'p, 'tcx> PatStack<'p, 'tcx> { @@ -351,15 +351,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { } fn empty() -> Self { - PatStack { patterns: smallvec![], head_ctors: smallvec![] } + PatStack { patterns: smallvec![], head_ctor: None } } fn from_vec(cx: &MatchCheckCtxt<'_, 'tcx>, patterns: SmallVec<[&'p Pat<'tcx>; 2]>) -> Self { if patterns.is_empty() { return PatStack::empty(); } - let head_ctors = pat_constructors(cx.tcx, cx.param_env, patterns[0]); - PatStack { patterns, head_ctors } + let head_ctor = Some(pat_constructors(cx.tcx, cx.param_env, patterns[0])); + PatStack { patterns, head_ctor } } fn from_slice(cx: &MatchCheckCtxt<'_, 'tcx>, s: &[&'p Pat<'tcx>]) -> Self { @@ -378,8 +378,8 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { self.patterns[0] } - fn head_ctors(&self) -> &SmallVec<[Constructor<'tcx>; 1]> { - &self.head_ctors + fn head_ctors(&self) -> &Constructor<'tcx> { + self.head_ctor.as_ref().unwrap() } fn iter(&self) -> impl Iterator> { @@ -392,20 +392,17 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> { cx: &MatchCheckCtxt<'a, 'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'q Pat<'tcx>], - ) -> SmallVec<[PatStack<'q, 'tcx>; 1]> + ) -> Option> where 'a: 'q, 'p: 'q, { - let new_heads = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); - let result = new_heads - .into_iter() - .map(|new_head| { - let mut pats = new_head.patterns; - pats.extend_from_slice(&self.patterns[1..]); - PatStack::from_vec(cx, pats) - }) - .collect(); + let new_head = specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns); + let result = new_head.map(|new_head| { + let mut pats = new_head.patterns; + pats.extend_from_slice(&self.patterns[1..]); + PatStack::from_vec(cx, pats) + }); debug!("specialize({:#?}, {:#?}) = {:#?}", self, constructor, result); result } @@ -435,7 +432,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { } fn head_ctors(&self) -> Vec<&Constructor<'tcx>> { - self.0.iter().flat_map(|r| r.head_ctors()).filter(|ctor| !ctor.is_wildcard()).collect() + self.0.iter().map(|r| r.head_ctors()).filter(|ctor| !ctor.is_wildcard()).collect() } /// This computes `S(constructor, self)`. See top of the file for explanations. @@ -452,7 +449,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> { Matrix( self.0 .iter() - .flat_map(|r| r.specialize(cx, constructor, ctor_wild_subpatterns)) + .filter_map(|r| r.specialize(cx, constructor, ctor_wild_subpatterns)) .collect(), ) } @@ -707,8 +704,8 @@ impl<'tcx> Constructor<'tcx> { let row_borders = head_ctors .iter() .map(|c| *c) - .flat_map(IntRange::from_ctor) - .flat_map(|range| ctor_range.intersection(cx.tcx, &range)) + .filter_map(IntRange::from_ctor) + .filter_map(|range| ctor_range.intersection(cx.tcx, &range)) .flat_map(|range| range_borders(range)); let ctor_borders = range_borders(ctor_range.clone()); let mut borders: Vec<_> = row_borders.chain(ctor_borders).collect(); @@ -1024,7 +1021,7 @@ impl<'tcx> Constructor<'tcx> { } } IntRange(range) => { - let used_ranges = used_ctors.iter().flat_map(IntRange::from_ctor); + let used_ranges = used_ctors.iter().filter_map(IntRange::from_ctor); let mut remaining_ranges: SmallVec<[IntRange<'tcx>; 1]> = smallvec![range]; // For each used ctor, subtract from the current set of constructors. @@ -1763,12 +1760,9 @@ pub fn is_useful<'p, 'a, 'tcx>( debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", ty, v.head()); - let v_constructors = v.head_ctors(); + let v_constructor = v.head_ctors(); - if cx.is_non_exhaustive_variant(v.head()) - && !cx.is_local(ty) - && !v_constructors.iter().any(|ctor| ctor.is_wildcard()) - { + if cx.is_non_exhaustive_variant(v.head()) && !cx.is_local(ty) && !v_constructor.is_wildcard() { debug!("is_useful - shortcut because declared non-exhaustive"); // FIXME(#65157) return Useful; @@ -1777,9 +1771,9 @@ pub fn is_useful<'p, 'a, 'tcx>( let matrix_head_ctors = matrix.head_ctors(); debug!("matrix_head_ctors = {:#?}", matrix_head_ctors); - v_constructors - .iter() - .flat_map(|ctor| ctor.split_meta_constructor(cx, ty, &matrix_head_ctors)) + v_constructor + .split_meta_constructor(cx, ty, &matrix_head_ctors) + .into_iter() .map(|c| is_useful_specialized(cx, matrix, v, c, ty, witness_preference)) .find(|result| result.is_useful()) .unwrap_or(NotUseful) @@ -1816,41 +1810,41 @@ fn pat_constructors<'tcx>( tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, pat: &Pat<'tcx>, -) -> SmallVec<[Constructor<'tcx>; 1]> { +) -> Constructor<'tcx> { match *pat.kind { PatKind::AscribeUserType { ref subpattern, .. } => { pat_constructors(tcx, param_env, subpattern) } - PatKind::Binding { .. } | PatKind::Wild => smallvec![Wildcard], - PatKind::Leaf { .. } | PatKind::Deref { .. } => smallvec![Single], + PatKind::Binding { .. } | PatKind::Wild => Wildcard, + PatKind::Leaf { .. } | PatKind::Deref { .. } => Single, PatKind::Variant { adt_def, variant_index, .. } => { - smallvec![Variant(adt_def.variants[variant_index].def_id)] + Variant(adt_def.variants[variant_index].def_id) } PatKind::Constant { value } => { if let Some(range) = IntRange::from_const(tcx, param_env, value) { - smallvec![IntRange(range)] + IntRange(range) } else { - smallvec![ConstantValue(value)] + ConstantValue(value) } } PatKind::Range(PatRange { lo, hi, end }) => { if let Some(range) = IntRange::from_const_range(tcx, param_env, &lo, &hi, &end) { - smallvec![IntRange(range)] + IntRange(range) } else { - smallvec![ConstantRange(lo, hi, end)] + ConstantRange(lo, hi, end) } } PatKind::Array { .. } => match pat.ty.kind { - ty::Array(_, length) => smallvec![FixedLenSlice(length.eval_usize(tcx, param_env))], + ty::Array(_, length) => FixedLenSlice(length.eval_usize(tcx, param_env)), _ => span_bug!(pat.span, "bad ty {:?} for array pattern", pat.ty), }, PatKind::Slice { ref prefix, ref slice, ref suffix } => { let prefix = prefix.len() as u64; let suffix = suffix.len() as u64; if slice.is_some() { - smallvec![VarLenSlice(prefix, suffix)] + VarLenSlice(prefix, suffix) } else { - smallvec![FixedLenSlice(prefix + suffix)] + FixedLenSlice(prefix + suffix) } } PatKind::Or { .. } => { @@ -2005,7 +1999,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( mut pat: &'q Pat<'tcx>, constructor: &Constructor<'tcx>, ctor_wild_subpatterns: &[&'p Pat<'tcx>], -) -> SmallVec<[PatStack<'p, 'tcx>; 1]> { +) -> Option> { while let PatKind::AscribeUserType { ref subpattern, .. } = *pat.kind { pat = subpattern; } @@ -2018,8 +2012,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( // If `constructor` is `MissingConstructors(_)`: by the invariant of MissingConstructors, // we know that all non-wildcard constructors should be discarded. return match *pat.kind { - PatKind::Binding { .. } | PatKind::Wild => smallvec![PatStack::empty()], - _ => smallvec![], + PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), + _ => None, }; } @@ -2027,23 +2021,23 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( PatKind::AscribeUserType { .. } => unreachable!(), // Handled above PatKind::Binding { .. } | PatKind::Wild => { - smallvec![PatStack::from_slice(cx, ctor_wild_subpatterns)] + Some(PatStack::from_slice(cx, ctor_wild_subpatterns)) } PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; if Variant(variant.def_id) == *constructor { - smallvec![patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns)] + Some(patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns)) } else { - smallvec![] + None } } PatKind::Leaf { ref subpatterns } => { - smallvec![patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns)] + Some(patterns_for_variant(cx, subpatterns, ctor_wild_subpatterns)) } - PatKind::Deref { ref subpattern } => smallvec![PatStack::from_pattern(cx, subpattern)], + PatKind::Deref { ref subpattern } => Some(PatStack::from_pattern(cx, subpattern)), PatKind::Constant { value } if constructor.is_slice() => { // We extract an `Option` for the pointer because slices of zero @@ -2064,7 +2058,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( } ConstValue::ByRef { .. } => { // FIXME(oli-obk): implement `deref` for `ConstValue` - return smallvec![]; + return None; } _ => span_bug!( pat.span, @@ -2085,7 +2079,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( let layout = if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty)) { layout } else { - return smallvec![]; + return None; }; let ptr = Pointer::new(AllocId(0), offset); let stack: Option> = (0..n) @@ -2100,11 +2094,11 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( }) .collect(); match stack { - Some(v) => smallvec![PatStack::from_vec(cx, v)], - None => smallvec![], + Some(v) => Some(PatStack::from_vec(cx, v)), + None => None, } } else { - smallvec![] + None } } @@ -2113,9 +2107,9 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( // - Single value: add a row if the pattern contains the constructor. // - Range: add a row if the constructor intersects the pattern. if let Some(ps) = constructor_intersects_pattern(cx, constructor, pat) { - smallvec![ps] + Some(ps) } else { - smallvec![] + None } } @@ -2125,7 +2119,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( let pat_len = prefix.len() + suffix.len(); if let Some(slice_count) = ctor_wild_subpatterns.len().checked_sub(pat_len) { if slice_count == 0 || slice.is_some() { - smallvec![PatStack::from_vec( + Some(PatStack::from_vec( cx, prefix .iter() @@ -2138,12 +2132,12 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( .chain(suffix.iter()), ) .collect(), - )] + )) } else { - smallvec![] + None } } else { - smallvec![] + None } } ConstantValue(cv) => { @@ -2156,8 +2150,8 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( suffix, cx.param_env, ) { - Ok(true) => smallvec![PatStack::default()], - Ok(false) | Err(ErrorReported) => smallvec![], + Ok(true) => Some(PatStack::default()), + Ok(false) | Err(ErrorReported) => None, } } _ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor), From fd3ec66b28680cd822d5014722bc83bd88c60a9d Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Fri, 18 Oct 2019 12:45:12 +0100 Subject: [PATCH 87/87] Integrate comment fixes suggested by arielb1 --- src/librustc_mir/hair/pattern/_match.rs | 65 ++++++++++++++++++++++--- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index eaf471454d285..79e040925085b 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -222,6 +222,59 @@ /// wildcards. Thus this mostly extends the original algorithm to ranges and variable-length /// slices, while removing the special-casing of the wildcard pattern. We also additionally /// support uninhabited types. +/// +/// +/// # Handling of missing constructors (MISSING-CTOR) +/// +/// This algorithm can be seen to essentially explore the full decision tree of patterns, except in +/// the case where it "takes a shortcut" to find a "missing" constructor - that is, a constructor +/// that is not covered by any of the non-wildcard patterns. +/// +/// For example, in the following case: +/// ```rust +/// match x { +/// (_, false) => {} +/// (None, false) => {} +/// } +/// ``` +/// +/// The algorithm proceeds as follows: +/// ``` +/// M = [ [(_, false)], +/// [(None, false)]] +/// p = [_] +/// -- expand tuple (ctor = Simple) +/// M = [ [_, false], +/// [None, false]] +/// p = [_, _] +/// -- `Some(_)` is a missing ctor, dropping all the non-wildcard arms +/// M = [ [false]] +/// p = [_] +/// -- `true` is a possible witness +/// M = [] +/// p = [] +/// return "[]" +/// return "[true]" +/// return "[Some(_), true]" +/// return "[(Some(_), true)]" +/// ``` +/// +/// Once it finds that `Some(_)` is a missing constructor, it does not need to look any further - +/// any witness using a non-missing constructor can be transformed to a witness using a missing +/// constructor - and therefore it does not try to look for witnesses involving the other +/// constructors - in this case, the `(None, true)` witness (which can be "transformed" to +/// `(Some(_), true)`). +/// +/// In the code, missing constructors are represented by the `Wildcard` and `MissingConstructors` +/// variants of `Constructor`, with the difference between them being down to error reporting: +/// `MissingConstructors` "remembers" the set of constructors it contains for error reporting (so +/// we can show the `... not covered` error message), while `Wildcard` doesn't. +/// +/// Therefore, `Wildcard` is used in cases where the exact constructor doesn't matter - either +/// where the head column of the matrix contains only wildcards (and therefore, *every* constructor +/// will work) or when the enum is `#[non_exhaustive]`, and therefore from a user POV there can +/// always assumed to be an "fresh" constructor that will be useful for the witness. +/// `MissingConstructors` is used in the other cases. use self::Constructor::*; use self::Usefulness::*; use self::WitnessPreference::*; @@ -1821,6 +1874,7 @@ fn pat_constructors<'tcx>( Variant(adt_def.variants[variant_index].def_id) } PatKind::Constant { value } => { + // FIXME: consts are not handled properly; see #65413 if let Some(range) = IntRange::from_const(tcx, param_env, value) { IntRange(range) } else { @@ -2005,12 +2059,9 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( } if let Wildcard | MissingConstructors(_) = constructor { - // If `constructor` is `Wildcard`: either there were only wildcards in the first component - // of the matrix, or we are in a special non_exhaustive case where we pretend the type has - // an extra `_` constructor to prevent exhaustive matching. In both cases, all non-wildcard - // constructors should be discarded. - // If `constructor` is `MissingConstructors(_)`: by the invariant of MissingConstructors, - // we know that all non-wildcard constructors should be discarded. + // Both those cases capture a set of constructors that are not present in the head of + // current matrix. This means that we discard all non-wildcard constructors. + // See (MISSING-CTOR) at the top of the file for more details. return match *pat.kind { PatKind::Binding { .. } | PatKind::Wild => Some(PatStack::empty()), _ => None, @@ -2057,7 +2108,7 @@ fn specialize_one_pattern<'p, 'a: 'p, 'q: 'p, 'tcx>( (data, Size::from_bytes(start as u64), (end - start) as u64, t) } ConstValue::ByRef { .. } => { - // FIXME(oli-obk): implement `deref` for `ConstValue` + // FIXME(oli-obk): implement `deref` for `ConstValue`. See #53708 return None; } _ => span_bug!(