From 54fa70290d8ab65e33116eb888278f7a64ca6da4 Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Tue, 20 Oct 2020 02:56:40 +0100 Subject: [PATCH] Unify the paths through `is_useful` --- .../src/thir/pattern/_match.rs | 156 ++++++------------ 1 file changed, 53 insertions(+), 103 deletions(-) diff --git a/compiler/rustc_mir_build/src/thir/pattern/_match.rs b/compiler/rustc_mir_build/src/thir/pattern/_match.rs index 948c441f2c9..c7f09ea4ad9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/_match.rs @@ -181,7 +181,6 @@ //! 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`. //! //! For example, if `P` is: //! @@ -1116,8 +1115,8 @@ impl<'tcx> Constructor<'tcx> { } } - /// Some constructors (namely IntRange and Slice) actually stand for a set of actual - /// constructors (integers and fixed-sized slices). When specializing for these + /// Some constructors (namely Wildcard, IntRange and Slice) actually stand for a set of actual + /// constructors (like variants, integers or fixed-sized slices). When specializing for these /// constructors, we want to be specialising for the actual underlying constructors. /// Naively, we would simply return the list of constructors they correspond to. We instead are /// more clever: if there are constructors that we know will behave the same wrt the current @@ -1136,6 +1135,7 @@ impl<'tcx> Constructor<'tcx> { debug!("Constructor::split({:#?}, {:#?})", self, pcx.matrix); match self { + Wildcard => Constructor::split_wildcard(pcx), // Fast-track if the range is trivial. In particular, we don't do the overlapping // ranges check. IntRange(ctor_range) @@ -1149,6 +1149,30 @@ impl<'tcx> Constructor<'tcx> { } } + /// For wildcards, there are two groups of constructors: there are the constructors actually + /// present in the matrix (`head_ctors`), and the constructors not present (`missing_ctors`). + /// Two constructors that are not in the matrix will either both be catched (by a wildcard), or + /// both not be catched. Therefore we can keep the missing constructors grouped together. + fn split_wildcard<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Self; 1]> { + // Missing constructors are those that are not matched by any non-wildcard patterns in the + // current column. We only fully construct them on-demand, because they're rarely used and + // can be big. + let missing_ctors = MissingConstructors::new(pcx); + + if missing_ctors.is_empty() { + // All the constructors are present in the matrix, so we just go through them all. + // We must also split them first. + // Since `all_ctors` never contains wildcards, this won't recurse more than once. + let (all_ctors, _) = missing_ctors.into_inner(); + all_ctors.into_iter().flat_map(|ctor| ctor.split(pcx, None)).collect() + } else { + // Some constructors are missing, thus we can specialize with the wildcard constructor, + // which will stand for those constructors that are missing, and behaves like any of + // them. + smallvec![Wildcard] + } + } + /// Returns whether `self` is covered by `other`, ie whether `self` is a subset of `other`. For /// the simple cases, this is simply checking for equality. For the "grouped" constructors, /// this checks for inclusion. @@ -1617,8 +1641,8 @@ impl<'tcx> Usefulness<'tcx> { match self { UsefulWithWitness(witnesses) => { let new_witnesses = if ctor.is_wildcard() { - let missing_ctors = MissingConstructors::new(pcx, is_top_level); - let new_patterns = missing_ctors.report_patterns(pcx); + let missing_ctors = MissingConstructors::new(pcx); + let new_patterns = missing_ctors.report_patterns(pcx, is_top_level); witnesses .into_iter() .flat_map(|witness| { @@ -2217,16 +2241,15 @@ impl<'tcx> std::cmp::PartialEq for IntRange<'tcx> { struct MissingConstructors<'tcx> { all_ctors: Vec>, used_ctors: Vec>, - is_top_level: bool, } impl<'tcx> MissingConstructors<'tcx> { - fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>, is_top_level: bool) -> Self { + fn new<'p>(pcx: PatCtxt<'_, 'p, 'tcx>) -> Self { let used_ctors: Vec> = pcx.matrix.head_ctors(pcx.cx).cloned().filter(|c| !c.is_wildcard()).collect(); let all_ctors = all_constructors(pcx); - MissingConstructors { all_ctors, used_ctors, is_top_level } + MissingConstructors { all_ctors, used_ctors } } fn into_inner(self) -> (Vec>, Vec>) { @@ -2244,7 +2267,11 @@ impl<'tcx> MissingConstructors<'tcx> { /// List the patterns corresponding to the missing constructors. In some cases, instead of /// listing all constructors of a given type, we prefer to simply report a wildcard. - fn report_patterns<'p>(&self, pcx: PatCtxt<'_, 'p, 'tcx>) -> SmallVec<[Pat<'tcx>; 1]> { + fn report_patterns<'p>( + &self, + pcx: PatCtxt<'_, 'p, 'tcx>, + is_top_level: bool, + ) -> SmallVec<[Pat<'tcx>; 1]> { // 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: @@ -2272,7 +2299,7 @@ impl<'tcx> MissingConstructors<'tcx> { // `used_ctors` is empty. // The exception is: if we are at the top-level, for example in an empty match, we // sometimes prefer reporting the list of constructors instead of just `_`. - let report_when_all_missing = self.is_top_level && !IntRange::is_integral(pcx.ty); + let report_when_all_missing = is_top_level && !IntRange::is_integral(pcx.ty); if self.used_ctors.is_empty() && !report_when_all_missing { // All constructors are unused. Report only a wildcard // rather than each individual constructor. @@ -2407,103 +2434,26 @@ crate fn is_useful<'p, 'tcx>( debug!("is_useful_expand_first_col: ty={:#?}, expanding {:#?}", pcx.ty, v.head()); - let constructor = v.head_ctor(cx); - let ret = if !constructor.is_wildcard() { - debug!("is_useful - expanding constructor: {:#?}", constructor); - constructor - .split(pcx, Some(hir_id)) - .into_iter() - .map(|c| { - is_useful_specialized( - pcx, - v, - &c, - witness_preference, - hir_id, - is_under_guard, - is_top_level, - ) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } else { - debug!("is_useful - expanding wildcard"); - - // `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` - - // Missing constructors are those that are not matched by any non-wildcard patterns in the - // current column. We only fully construct them on-demand, because they're rarely used and - // can be big. - let missing_ctors = MissingConstructors::new(pcx, is_top_level); - - debug!("is_useful_missing_ctors.empty()={:#?}", missing_ctors.is_empty(),); - - if missing_ctors.is_empty() { - let (all_ctors, _) = missing_ctors.into_inner(); - all_ctors - .into_iter() - .flat_map(|ctor| ctor.split(pcx, None)) - .map(|c| { - is_useful_specialized( - pcx, - v, - &c, - witness_preference, - hir_id, - is_under_guard, - is_top_level, - ) - }) - .find(|result| result.is_useful()) - .unwrap_or(NotUseful) - } else { - // Some constructors are missing, thus we can specialize with the wildcard constructor, - // which will stand for those constructors that are missing, and behaves like any of - // them. - is_useful_specialized( - pcx, - v, - constructor, - witness_preference, - hir_id, - is_under_guard, - is_top_level, - ) - } - }; + let ret = v + .head_ctor(cx) + .split(pcx, Some(hir_id)) + .into_iter() + .map(|ctor| { + // We cache the result of `Fields::wildcards` because it is used a lot. + let ctor_wild_subpatterns = Fields::wildcards(pcx, &ctor); + let matrix = pcx.matrix.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns); + // Unwrap is ok: v can always be specialized with its own constructor. + let v = v.specialize_constructor(pcx, &ctor, &ctor_wild_subpatterns, true).unwrap(); + let usefulness = + is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); + usefulness.apply_constructor(pcx, &ctor, &ctor_wild_subpatterns, is_top_level) + }) + .find(|result| result.is_useful()) + .unwrap_or(NotUseful); debug!("is_useful::returns({:#?}, {:#?}) = {:?}", matrix, v, ret); ret } -/// 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, 'tcx>( - pcx: PatCtxt<'_, 'p, 'tcx>, - v: &PatStack<'p, 'tcx>, - ctor: &Constructor<'tcx>, - witness_preference: WitnessPreference, - hir_id: HirId, - is_under_guard: bool, - is_top_level: bool, -) -> Usefulness<'tcx> { - debug!("is_useful_specialized({:#?}, {:#?}, {:?})", v, ctor, pcx.ty); - - // We cache the result of `Fields::wildcards` because it is used a lot. - let ctor_wild_subpatterns = Fields::wildcards(pcx, ctor); - let matrix = pcx.matrix.specialize_constructor(pcx, ctor, &ctor_wild_subpatterns); - // Unwrap is ok: v can always be specialized with its own constructor. - let v = v.specialize_constructor(pcx, ctor, &ctor_wild_subpatterns, true).unwrap(); - let usefulness = - is_useful(pcx.cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); - usefulness.apply_constructor(pcx, ctor, &ctor_wild_subpatterns, is_top_level) -} - /// Determines the constructor that the given pattern can be specialized to. /// Returns `None` in case of a catch-all, which can't be specialized. fn pat_constructor<'p, 'tcx>(