|
1 | | -//! Note: most of the tests relevant to this file can be found (at the time of writing) in |
2 | | -//! src/tests/ui/pattern/usefulness. |
| 1 | +//! Note: tests specific to this file can be found in: |
| 2 | +//! - ui/pattern/usefulness |
| 3 | +//! - ui/or-patterns |
| 4 | +//! - ui/consts/const_in_pattern |
| 5 | +//! - ui/rfc-2008-non-exhaustive |
| 6 | +//! - probably many others |
| 7 | +//! I (Nadrieril) prefer to put new tests in `ui/pattern/usefulness` unless there's a specific |
| 8 | +//! reason not to, for example if they depend on a particular feature like or_patterns. |
3 | 9 | //! |
4 | 10 | //! This file includes the logic for exhaustiveness and usefulness checking for |
5 | 11 | //! pattern-matching. Specifically, given a list of patterns for a type, we can |
@@ -1361,8 +1367,9 @@ impl<'p, 'tcx> Fields<'p, 'tcx> { |
1361 | 1367 |
|
1362 | 1368 | #[derive(Clone, Debug)] |
1363 | 1369 | crate enum Usefulness<'tcx> { |
1364 | | - /// Carries a list of unreachable subpatterns. Used only in the presence of or-patterns. |
1365 | | - Useful(Vec<Span>), |
| 1370 | + /// Carries, for each column in the matrix, a set of sub-branches that have been found to be |
| 1371 | + /// unreachable. Used only in the presence of or-patterns, otherwise it stays empty. |
| 1372 | + Useful(Vec<FxHashSet<Span>>), |
1366 | 1373 | /// Carries a list of witnesses of non-exhaustiveness. |
1367 | 1374 | UsefulWithWitness(Vec<Witness<'tcx>>), |
1368 | 1375 | NotUseful, |
@@ -1410,6 +1417,23 @@ impl<'tcx> Usefulness<'tcx> { |
1410 | 1417 | }; |
1411 | 1418 | UsefulWithWitness(new_witnesses) |
1412 | 1419 | } |
| 1420 | + Useful(mut unreachables) => { |
| 1421 | + if !unreachables.is_empty() { |
| 1422 | + // When we apply a constructor, there are `arity` columns of the matrix that |
| 1423 | + // corresponded to its arguments. All the unreachables found in these columns |
| 1424 | + // will, after `apply`, come from the first column. So we take the union of all |
| 1425 | + // the corresponding sets and put them in the first column. |
| 1426 | + // Note that `arity` may be 0, in which case we just push a new empty set. |
| 1427 | + let len = unreachables.len(); |
| 1428 | + let arity = ctor_wild_subpatterns.len(); |
| 1429 | + let mut unioned = FxHashSet::default(); |
| 1430 | + for set in unreachables.drain((len - arity)..) { |
| 1431 | + unioned.extend(set) |
| 1432 | + } |
| 1433 | + unreachables.push(unioned); |
| 1434 | + } |
| 1435 | + Useful(unreachables) |
| 1436 | + } |
1413 | 1437 | x => x, |
1414 | 1438 | } |
1415 | 1439 | } |
@@ -2091,55 +2115,87 @@ crate fn is_useful<'p, 'tcx>( |
2091 | 2115 |
|
2092 | 2116 | // If the first pattern is an or-pattern, expand it. |
2093 | 2117 | if let Some(vs) = v.expand_or_pat() { |
2094 | | - // We need to push the already-seen patterns into the matrix in order to detect redundant |
2095 | | - // branches like `Some(_) | Some(0)`. We also keep track of the unreachable subpatterns. |
2096 | | - let mut matrix = matrix.clone(); |
2097 | | - // `Vec` of all the unreachable branches of the current or-pattern. |
2098 | | - let mut unreachable_branches = Vec::new(); |
2099 | | - // Subpatterns that are unreachable from all branches. E.g. in the following case, the last |
2100 | | - // `true` is unreachable only from one branch, so it is overall reachable. |
| 2118 | + // We expand the or pattern, trying each of its branches in turn and keeping careful track |
| 2119 | + // of possible unreachable sub-branches. |
| 2120 | + // |
| 2121 | + // If two branches have detected some unreachable sub-branches, we need to be careful. If |
| 2122 | + // they were detected in columns that are not the current one, we want to keep only the |
| 2123 | + // sub-branches that were unreachable in _all_ branches. Eg. in the following, the last |
| 2124 | + // `true` is unreachable in the second branch of the first or-pattern, but not otherwise. |
| 2125 | + // Therefore we don't want to lint that it is unreachable. |
2101 | 2126 | // |
2102 | 2127 | // ``` |
2103 | 2128 | // match (true, true) { |
2104 | 2129 | // (true, true) => {} |
2105 | 2130 | // (false | true, false | true) => {} |
2106 | 2131 | // } |
2107 | 2132 | // ``` |
2108 | | - let mut unreachable_subpats = FxHashSet::default(); |
2109 | | - // Whether any branch at all is useful. |
| 2133 | + // If however the sub-branches come from the current column, they come from the inside of |
| 2134 | + // the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want |
| 2135 | + // to lint that the last `false` is unreachable. |
| 2136 | + // ``` |
| 2137 | + // match None { |
| 2138 | + // Some(false) => {} |
| 2139 | + // None | Some(true | false) => {} |
| 2140 | + // } |
| 2141 | + // ``` |
| 2142 | + |
| 2143 | + let mut matrix = matrix.clone(); |
| 2144 | + // We keep track of sub-branches separately depending on whether they come from this column |
| 2145 | + // or from others. |
| 2146 | + let mut unreachables_this_column: FxHashSet<Span> = FxHashSet::default(); |
| 2147 | + let mut unreachables_other_columns: Vec<FxHashSet<Span>> = Vec::default(); |
| 2148 | + // Whether at least one branch is reachable. |
2110 | 2149 | let mut any_is_useful = false; |
2111 | 2150 |
|
2112 | 2151 | for v in vs { |
2113 | 2152 | let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false); |
2114 | 2153 | match res { |
2115 | | - Useful(pats) => { |
2116 | | - if !any_is_useful { |
2117 | | - any_is_useful = true; |
2118 | | - // Initialize with the first set of unreachable subpatterns encountered. |
2119 | | - unreachable_subpats = pats.into_iter().collect(); |
2120 | | - } else { |
2121 | | - // Keep the patterns unreachable from both this and previous branches. |
2122 | | - unreachable_subpats = |
2123 | | - pats.into_iter().filter(|p| unreachable_subpats.contains(p)).collect(); |
| 2154 | + Useful(unreachables) => { |
| 2155 | + if let Some((this_column, other_columns)) = unreachables.split_last() { |
| 2156 | + // We keep the union of unreachables found in the first column. |
| 2157 | + unreachables_this_column.extend(this_column); |
| 2158 | + // We keep the intersection of unreachables found in other columns. |
| 2159 | + if unreachables_other_columns.is_empty() { |
| 2160 | + unreachables_other_columns = other_columns.to_vec(); |
| 2161 | + } else { |
| 2162 | + unreachables_other_columns = unreachables_other_columns |
| 2163 | + .into_iter() |
| 2164 | + .zip(other_columns) |
| 2165 | + .map(|(x, y)| x.intersection(&y).copied().collect()) |
| 2166 | + .collect(); |
| 2167 | + } |
2124 | 2168 | } |
| 2169 | + any_is_useful = true; |
2125 | 2170 | } |
2126 | | - NotUseful => unreachable_branches.push(v.head().span), |
2127 | | - UsefulWithWitness(_) => { |
2128 | | - bug!("Encountered or-pat in `v` during exhaustiveness checking") |
| 2171 | + NotUseful => { |
| 2172 | + unreachables_this_column.insert(v.head().span); |
2129 | 2173 | } |
| 2174 | + UsefulWithWitness(_) => bug!( |
| 2175 | + "encountered or-pat in the expansion of `_` during exhaustiveness checking" |
| 2176 | + ), |
2130 | 2177 | } |
2131 | | - // If pattern has a guard don't add it to the matrix |
| 2178 | + |
| 2179 | + // If pattern has a guard don't add it to the matrix. |
2132 | 2180 | if !is_under_guard { |
| 2181 | + // We push the already-seen patterns into the matrix in order to detect redundant |
| 2182 | + // branches like `Some(_) | Some(0)`. |
2133 | 2183 | matrix.push(v); |
2134 | 2184 | } |
2135 | 2185 | } |
2136 | | - if any_is_useful { |
2137 | | - // Collect all the unreachable patterns. |
2138 | | - unreachable_branches.extend(unreachable_subpats); |
2139 | | - return Useful(unreachable_branches); |
| 2186 | + |
| 2187 | + return if any_is_useful { |
| 2188 | + let mut unreachables = if unreachables_other_columns.is_empty() { |
| 2189 | + let n_columns = v.len(); |
| 2190 | + (0..n_columns - 1).map(|_| FxHashSet::default()).collect() |
| 2191 | + } else { |
| 2192 | + unreachables_other_columns |
| 2193 | + }; |
| 2194 | + unreachables.push(unreachables_this_column); |
| 2195 | + Useful(unreachables) |
2140 | 2196 | } else { |
2141 | | - return NotUseful; |
2142 | | - } |
| 2197 | + NotUseful |
| 2198 | + }; |
2143 | 2199 | } |
2144 | 2200 |
|
2145 | 2201 | // FIXME(Nadrieril): Hack to work around type normalization issues (see #72476). |
|
0 commit comments