From dcda83f6d4d7df1588293392f110cee5f9ca022f Mon Sep 17 00:00:00 2001 From: Nadrieril Date: Wed, 24 Jan 2024 23:08:36 +0100 Subject: [PATCH] Move usefulness-specific pattern computations to `usefulness` --- compiler/rustc_pattern_analysis/src/pat.rs | 37 +++++-------------- .../rustc_pattern_analysis/src/usefulness.rs | 37 ++++++++++++++++--- 2 files changed, 41 insertions(+), 33 deletions(-) diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs index 9bde23c7bf124..2314da7149b00 100644 --- a/compiler/rustc_pattern_analysis/src/pat.rs +++ b/compiler/rustc_pattern_analysis/src/pat.rs @@ -29,7 +29,7 @@ pub struct DeconstructedPat { /// correspond to a user-supplied pattern. data: Option, /// Whether removing this arm would change the behavior of the match expression. - useful: Cell, + pub(crate) useful: Cell, } impl DeconstructedPat { @@ -112,34 +112,17 @@ impl DeconstructedPat { pub(crate) fn set_useful(&self) { self.useful.set(true) } - pub(crate) fn is_useful(&self) -> bool { - if self.useful.get() { - true - } else if self.is_or_pat() && self.iter_fields().any(|f| f.is_useful()) { - // We always expand or patterns in the matrix, so we will never see the actual - // or-pattern (the one with constructor `Or`) in the column. As such, it will not be - // marked as useful itself, only its children will. We recover this information here. - self.set_useful(); - true - } else { - false + + /// Walk top-down and call `it` in each place where a pattern occurs + /// starting with the root pattern `walk` is called on. If `it` returns + /// false then we will descend no further but siblings will be processed. + pub fn walk<'a>(&'a self, it: &mut impl FnMut(&'a Self) -> bool) { + if !it(self) { + return; } - } - /// Report the subpatterns that were not useful, if any. - pub(crate) fn redundant_subpatterns(&self) -> Vec<&Self> { - let mut subpats = Vec::new(); - self.collect_redundant_subpatterns(&mut subpats); - subpats - } - fn collect_redundant_subpatterns<'a>(&'a self, subpats: &mut Vec<&'a Self>) { - // We don't look at subpatterns if we already reported the whole pattern as redundant. - if !self.is_useful() { - subpats.push(self); - } else { - for p in self.iter_fields() { - p.collect_redundant_subpatterns(subpats); - } + for p in self.iter_fields() { + p.walk(it) } } } diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs index 33df4ebea43d7..577373cbaa250 100644 --- a/compiler/rustc_pattern_analysis/src/usefulness.rs +++ b/compiler/rustc_pattern_analysis/src/usefulness.rs @@ -1581,6 +1581,36 @@ pub enum Usefulness<'p, Cx: TypeCx> { Redundant, } +/// Report whether this pattern was found useful, and its subpatterns that were not useful if any. +fn collect_pattern_usefulness<'p, Cx: TypeCx>(pat: &'p DeconstructedPat) -> Usefulness<'p, Cx> { + fn pat_is_useful<'p, Cx: TypeCx>(pat: &'p DeconstructedPat) -> bool { + if pat.useful.get() { + true + } else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(f)) { + // We always expand or patterns in the matrix, so we will never see the actual + // or-pattern (the one with constructor `Or`) in the column. As such, it will not be + // marked as useful itself, only its children will. We recover this information here. + true + } else { + false + } + } + + let mut subpats = Vec::new(); + pat.walk(&mut |p| { + if pat_is_useful(p) { + // The pattern is useful, so we recurse to find redundant subpatterns. + true + } else { + // The pattern is redundant. + subpats.push(p); + false // stop recursing + } + }); + + if pat_is_useful(pat) { Usefulness::Useful(subpats) } else { Usefulness::Redundant } +} + /// The output of checking a match for exhaustiveness and arm usefulness. pub struct UsefulnessReport<'p, Cx: TypeCx> { /// For each arm of the input, whether that arm is useful after the arms above it. @@ -1609,12 +1639,7 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>( .copied() .map(|arm| { debug!(?arm); - // We warn when a pattern is not useful. - let usefulness = if arm.pat.is_useful() { - Usefulness::Useful(arm.pat.redundant_subpatterns()) - } else { - Usefulness::Redundant - }; + let usefulness = collect_pattern_usefulness(arm.pat); (arm, usefulness) }) .collect();