|
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