aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_ty/src/diagnostics/pattern/usefulness.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_ty/src/diagnostics/pattern/usefulness.rs')
-rw-r--r--crates/hir_ty/src/diagnostics/pattern/usefulness.rs736
1 files changed, 736 insertions, 0 deletions
diff --git a/crates/hir_ty/src/diagnostics/pattern/usefulness.rs b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs
new file mode 100644
index 000000000..f5f6bf494
--- /dev/null
+++ b/crates/hir_ty/src/diagnostics/pattern/usefulness.rs
@@ -0,0 +1,736 @@
1// Based on rust-lang/rust 1.52.0-nightly (25c15cdbe 2021-04-22)
2// rust/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
3
4use std::{cell::RefCell, iter::FromIterator, ops::Index, sync::Arc};
5
6use base_db::CrateId;
7use hir_def::{
8 body::Body,
9 expr::{ExprId, Pat, PatId},
10};
11use la_arena::Arena;
12use once_cell::unsync::OnceCell;
13use rustc_hash::FxHashMap;
14use smallvec::{smallvec, SmallVec};
15
16use crate::{db::HirDatabase, InferenceResult, Ty};
17
18use super::deconstruct_pat::{Constructor, Fields, SplitWildcard};
19
20use self::{
21 helper::{Captures, PatIdExt},
22 Usefulness::*,
23 WitnessPreference::*,
24};
25
26pub(crate) struct MatchCheckCtx<'a> {
27 pub(crate) krate: CrateId,
28 pub(crate) match_expr: ExprId,
29 pub(crate) body: Arc<Body>,
30 pub(crate) infer: &'a InferenceResult,
31 pub(crate) db: &'a dyn HirDatabase,
32 /// Patterns from self.body.pats plus generated by the check.
33 pub(crate) pattern_arena: &'a RefCell<PatternArena>,
34}
35
36impl<'a> MatchCheckCtx<'a> {
37 pub(super) fn is_uninhabited(&self, ty: &Ty) -> bool {
38 // FIXME(iDawer) implement exhaustive_patterns feature. More info in:
39 // Tracking issue for RFC 1872: exhaustive_patterns feature https://github.com/rust-lang/rust/issues/51085
40 false
41 }
42
43 pub(super) fn alloc_pat(&self, pat: Pat, ty: &Ty) -> PatId {
44 self.pattern_arena.borrow_mut().alloc(pat, ty)
45 }
46
47 /// Get type of a pattern. Handles expanded patterns.
48 pub(super) fn type_of(&self, pat: PatId) -> Ty {
49 let type_of_expanded_pat = self.pattern_arena.borrow().type_of_epat.get(&pat).cloned();
50 type_of_expanded_pat.unwrap_or_else(|| self.infer[pat].clone())
51 }
52}
53
54#[derive(Clone)]
55pub(super) struct PatCtxt<'a> {
56 pub(super) cx: &'a MatchCheckCtx<'a>,
57 /// Type of the current column under investigation.
58 pub(super) ty: Ty,
59 /// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
60 /// subpattern.
61 pub(super) is_top_level: bool,
62}
63
64impl PatIdExt for PatId {
65 fn is_wildcard(self, cx: &MatchCheckCtx<'_>) -> bool {
66 matches!(cx.pattern_arena.borrow()[self], Pat::Bind { subpat: None, .. } | Pat::Wild)
67 }
68
69 fn is_or_pat(self, cx: &MatchCheckCtx<'_>) -> bool {
70 matches!(cx.pattern_arena.borrow()[self], Pat::Or(..))
71 }
72
73 /// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
74 fn expand_or_pat(self, cx: &MatchCheckCtx<'_>) -> Vec<Self> {
75 fn expand(pat: PatId, vec: &mut Vec<PatId>, pat_arena: &PatternArena) {
76 if let Pat::Or(pats) = &pat_arena[pat] {
77 for &pat in pats {
78 expand(pat, vec, pat_arena);
79 }
80 } else {
81 vec.push(pat)
82 }
83 }
84
85 let pat_arena = cx.pattern_arena.borrow();
86 let mut pats = Vec::new();
87 expand(self, &mut pats, &pat_arena);
88 pats
89 }
90}
91
92/// A row of a matrix. Rows of len 1 are very common, which is why `SmallVec[_; 2]`
93/// works well.
94#[derive(Clone)]
95pub(super) struct PatStack {
96 pats: SmallVec<[PatId; 2]>,
97 /// Cache for the constructor of the head
98 head_ctor: OnceCell<Constructor>,
99}
100
101impl PatStack {
102 fn from_pattern(pat: PatId) -> Self {
103 Self::from_vec(smallvec![pat])
104 }
105
106 fn from_vec(vec: SmallVec<[PatId; 2]>) -> Self {
107 PatStack { pats: vec, head_ctor: OnceCell::new() }
108 }
109
110 fn is_empty(&self) -> bool {
111 self.pats.is_empty()
112 }
113
114 fn len(&self) -> usize {
115 self.pats.len()
116 }
117
118 fn head(&self) -> PatId {
119 self.pats[0]
120 }
121
122 #[inline]
123 fn head_ctor(&self, cx: &MatchCheckCtx<'_>) -> &Constructor {
124 self.head_ctor.get_or_init(|| Constructor::from_pat(cx, self.head()))
125 }
126
127 fn iter(&self) -> impl Iterator<Item = PatId> + '_ {
128 self.pats.iter().copied()
129 }
130
131 // Recursively expand the first pattern into its subpatterns. Only useful if the pattern is an
132 // or-pattern. Panics if `self` is empty.
133 fn expand_or_pat(&self, cx: &MatchCheckCtx<'_>) -> impl Iterator<Item = PatStack> + '_ {
134 self.head().expand_or_pat(cx).into_iter().map(move |pat| {
135 let mut new_patstack = PatStack::from_pattern(pat);
136 new_patstack.pats.extend_from_slice(&self.pats[1..]);
137 new_patstack
138 })
139 }
140
141 /// This computes `S(self.head_ctor(), self)`. See top of the file for explanations.
142 ///
143 /// Structure patterns with a partial wild pattern (Foo { a: 42, .. }) have their missing
144 /// fields filled with wild patterns.
145 ///
146 /// This is roughly the inverse of `Constructor::apply`.
147 fn pop_head_constructor(
148 &self,
149 ctor_wild_subpatterns: &Fields,
150 cx: &MatchCheckCtx<'_>,
151 ) -> PatStack {
152 // We pop the head pattern and push the new fields extracted from the arguments of
153 // `self.head()`.
154 let mut new_fields =
155 ctor_wild_subpatterns.replace_with_pattern_arguments(self.head(), cx).into_patterns();
156 new_fields.extend_from_slice(&self.pats[1..]);
157 PatStack::from_vec(new_fields)
158 }
159}
160
161impl Default for PatStack {
162 fn default() -> Self {
163 Self::from_vec(smallvec![])
164 }
165}
166
167impl PartialEq for PatStack {
168 fn eq(&self, other: &Self) -> bool {
169 self.pats == other.pats
170 }
171}
172
173impl FromIterator<PatId> for PatStack {
174 fn from_iter<T>(iter: T) -> Self
175 where
176 T: IntoIterator<Item = PatId>,
177 {
178 Self::from_vec(iter.into_iter().collect())
179 }
180}
181
182#[derive(Clone)]
183pub(super) struct Matrix {
184 patterns: Vec<PatStack>,
185}
186
187impl Matrix {
188 fn empty() -> Self {
189 Matrix { patterns: vec![] }
190 }
191
192 /// Number of columns of this matrix. `None` is the matrix is empty.
193 pub(super) fn column_count(&self) -> Option<usize> {
194 self.patterns.get(0).map(|r| r.len())
195 }
196
197 /// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
198 /// expands it.
199 fn push(&mut self, row: PatStack, cx: &MatchCheckCtx<'_>) {
200 if !row.is_empty() && row.head().is_or_pat(cx) {
201 for row in row.expand_or_pat(cx) {
202 self.patterns.push(row);
203 }
204 } else {
205 self.patterns.push(row);
206 }
207 }
208
209 /// Iterate over the first component of each row
210 fn heads(&self) -> impl Iterator<Item = PatId> + '_ {
211 self.patterns.iter().map(|r| r.head())
212 }
213
214 /// Iterate over the first constructor of each row.
215 fn head_ctors<'a>(
216 &'a self,
217 cx: &'a MatchCheckCtx<'_>,
218 ) -> impl Iterator<Item = &'a Constructor> + Clone {
219 self.patterns.iter().map(move |r| r.head_ctor(cx))
220 }
221
222 /// This computes `S(constructor, self)`. See top of the file for explanations.
223 fn specialize_constructor(
224 &self,
225 pcx: &PatCtxt<'_>,
226 ctor: &Constructor,
227 ctor_wild_subpatterns: &Fields,
228 ) -> Matrix {
229 let rows = self
230 .patterns
231 .iter()
232 .filter(|r| ctor.is_covered_by(pcx, r.head_ctor(pcx.cx)))
233 .map(|r| r.pop_head_constructor(ctor_wild_subpatterns, pcx.cx));
234 Matrix::from_iter(rows, pcx.cx)
235 }
236
237 fn from_iter(rows: impl IntoIterator<Item = PatStack>, cx: &MatchCheckCtx<'_>) -> Matrix {
238 let mut matrix = Matrix::empty();
239 for x in rows {
240 // Using `push` ensures we correctly expand or-patterns.
241 matrix.push(x, cx);
242 }
243 matrix
244 }
245}
246
247#[derive(Debug, Clone)]
248enum SubPatSet {
249 /// The empty set. This means the pattern is unreachable.
250 Empty,
251 /// The set containing the full pattern.
252 Full,
253 /// If the pattern is a pattern with a constructor or a pattern-stack, we store a set for each
254 /// of its subpatterns. Missing entries in the map are implicitly full, because that's the
255 /// common case.
256 Seq { subpats: FxHashMap<usize, SubPatSet> },
257 /// If the pattern is an or-pattern, we store a set for each of its alternatives. Missing
258 /// entries in the map are implicitly empty. Note: we always flatten nested or-patterns.
259 Alt {
260 subpats: FxHashMap<usize, SubPatSet>,
261 /// Counts the total number of alternatives in the pattern
262 alt_count: usize,
263 /// We keep the pattern around to retrieve spans.
264 pat: PatId,
265 },
266}
267
268impl SubPatSet {
269 fn full() -> Self {
270 SubPatSet::Full
271 }
272
273 fn empty() -> Self {
274 SubPatSet::Empty
275 }
276
277 fn is_empty(&self) -> bool {
278 match self {
279 SubPatSet::Empty => true,
280 SubPatSet::Full => false,
281 // If any subpattern in a sequence is unreachable, the whole pattern is unreachable.
282 SubPatSet::Seq { subpats } => subpats.values().any(|set| set.is_empty()),
283 // An or-pattern is reachable if any of its alternatives is.
284 SubPatSet::Alt { subpats, .. } => subpats.values().all(|set| set.is_empty()),
285 }
286 }
287
288 fn is_full(&self) -> bool {
289 match self {
290 SubPatSet::Empty => false,
291 SubPatSet::Full => true,
292 // The whole pattern is reachable only when all its alternatives are.
293 SubPatSet::Seq { subpats } => subpats.values().all(|sub_set| sub_set.is_full()),
294 // The whole or-pattern is reachable only when all its alternatives are.
295 SubPatSet::Alt { subpats, alt_count, .. } => {
296 subpats.len() == *alt_count && subpats.values().all(|set| set.is_full())
297 }
298 }
299 }
300
301 /// Union `self` with `other`, mutating `self`.
302 fn union(&mut self, other: Self) {
303 use SubPatSet::*;
304 // Union with full stays full; union with empty changes nothing.
305 if self.is_full() || other.is_empty() {
306 return;
307 } else if self.is_empty() {
308 *self = other;
309 return;
310 } else if other.is_full() {
311 *self = Full;
312 return;
313 }
314
315 match (&mut *self, other) {
316 (Seq { .. }, Seq { .. }) => {
317 todo!()
318 }
319 (Alt { .. }, Alt { .. }) => {
320 todo!()
321 }
322 _ => panic!("bug"),
323 }
324 }
325
326 /// Returns a list of the spans of the unreachable subpatterns. If `self` is empty (i.e. the
327 /// whole pattern is unreachable) we return `None`.
328 fn list_unreachable_spans(&self) -> Option<Vec<()>> {
329 if self.is_empty() {
330 return None;
331 }
332 if self.is_full() {
333 // No subpatterns are unreachable.
334 return Some(Vec::new());
335 }
336 todo!()
337 }
338
339 /// When `self` refers to a patstack that was obtained from specialization, after running
340 /// `unspecialize` it will refer to the original patstack before specialization.
341 fn unspecialize(self, arity: usize) -> Self {
342 use SubPatSet::*;
343 match self {
344 Full => Full,
345 Empty => Empty,
346 Seq { subpats } => {
347 todo!()
348 }
349 Alt { .. } => panic!("bug"),
350 }
351 }
352
353 /// When `self` refers to a patstack that was obtained from splitting an or-pattern, after
354 /// running `unspecialize` it will refer to the original patstack before splitting.
355 ///
356 /// For example:
357 /// ```
358 /// match Some(true) {
359 /// Some(true) => {}
360 /// None | Some(true | false) => {}
361 /// }
362 /// ```
363 /// Here `None` would return the full set and `Some(true | false)` would return the set
364 /// containing `false`. After `unsplit_or_pat`, we want the set to contain `None` and `false`.
365 /// This is what this function does.
366 fn unsplit_or_pat(mut self, alt_id: usize, alt_count: usize, pat: PatId) -> Self {
367 todo!()
368 }
369}
370
371/// This carries the results of computing usefulness, as described at the top of the file. When
372/// checking usefulness of a match branch, we use the `NoWitnesses` variant, which also keeps track
373/// of potential unreachable sub-patterns (in the presence of or-patterns). When checking
374/// exhaustiveness of a whole match, we use the `WithWitnesses` variant, which carries a list of
375/// witnesses of non-exhaustiveness when there are any.
376/// Which variant to use is dictated by `WitnessPreference`.
377#[derive(Clone, Debug)]
378enum Usefulness {
379 /// Carries a set of subpatterns that have been found to be reachable. If empty, this indicates
380 /// the whole pattern is unreachable. If not, this indicates that the pattern is reachable but
381 /// that some sub-patterns may be unreachable (due to or-patterns). In the absence of
382 /// or-patterns this will always be either `Empty` (the whole pattern is unreachable) or `Full`
383 /// (the whole pattern is reachable).
384 NoWitnesses(SubPatSet),
385 /// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
386 /// pattern is unreachable.
387 WithWitnesses(Vec<Witness>),
388}
389
390impl Usefulness {
391 fn new_useful(preference: WitnessPreference) -> Self {
392 match preference {
393 ConstructWitness => WithWitnesses(vec![Witness(vec![])]),
394 LeaveOutWitness => NoWitnesses(SubPatSet::full()),
395 }
396 }
397 fn new_not_useful(preference: WitnessPreference) -> Self {
398 match preference {
399 ConstructWitness => WithWitnesses(vec![]),
400 LeaveOutWitness => NoWitnesses(SubPatSet::empty()),
401 }
402 }
403
404 /// Combine usefulnesses from two branches. This is an associative operation.
405 fn extend(&mut self, other: Self) {
406 match (&mut *self, other) {
407 (WithWitnesses(_), WithWitnesses(o)) if o.is_empty() => {}
408 (WithWitnesses(s), WithWitnesses(o)) if s.is_empty() => *self = WithWitnesses(o),
409 (WithWitnesses(s), WithWitnesses(o)) => s.extend(o),
410 (NoWitnesses(s), NoWitnesses(o)) => s.union(o),
411 _ => unreachable!(),
412 }
413 }
414
415 /// When trying several branches and each returns a `Usefulness`, we need to combine the
416 /// results together.
417 fn merge(pref: WitnessPreference, usefulnesses: impl Iterator<Item = Self>) -> Self {
418 let mut ret = Self::new_not_useful(pref);
419 for u in usefulnesses {
420 ret.extend(u);
421 if let NoWitnesses(subpats) = &ret {
422 if subpats.is_full() {
423 // Once we reach the full set, more unions won't change the result.
424 return ret;
425 }
426 }
427 }
428 ret
429 }
430
431 /// After calculating the usefulness for a branch of an or-pattern, call this to make this
432 /// usefulness mergeable with those from the other branches.
433 fn unsplit_or_pat(self, alt_id: usize, alt_count: usize, pat: PatId) -> Self {
434 match self {
435 NoWitnesses(subpats) => NoWitnesses(subpats.unsplit_or_pat(alt_id, alt_count, pat)),
436 WithWitnesses(_) => panic!("bug"),
437 }
438 }
439
440 /// After calculating usefulness after a specialization, call this to recontruct a usefulness
441 /// that makes sense for the matrix pre-specialization. This new usefulness can then be merged
442 /// with the results of specializing with the other constructors.
443 fn apply_constructor(
444 self,
445 pcx: &PatCtxt<'_>,
446 matrix: &Matrix,
447 ctor: &Constructor,
448 ctor_wild_subpatterns: &Fields,
449 ) -> Self {
450 match self {
451 WithWitnesses(witnesses) if witnesses.is_empty() => WithWitnesses(witnesses),
452 WithWitnesses(w) => {
453 let new_witnesses = if matches!(ctor, Constructor::Missing) {
454 let mut split_wildcard = SplitWildcard::new(pcx);
455 split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
456 } else {
457 todo!("Usefulness::apply_constructor({:?})", ctor)
458 };
459 todo!("Usefulness::apply_constructor({:?})", ctor)
460 }
461 NoWitnesses(subpats) => NoWitnesses(subpats.unspecialize(ctor_wild_subpatterns.len())),
462 }
463 }
464}
465
466#[derive(Copy, Clone, Debug)]
467enum WitnessPreference {
468 ConstructWitness,
469 LeaveOutWitness,
470}
471
472#[derive(Clone, Debug)]
473pub(crate) struct Witness(Vec<Pat>);
474
475impl Witness {
476 /// Asserts that the witness contains a single pattern, and returns it.
477 fn single_pattern(self) -> Pat {
478 assert_eq!(self.0.len(), 1);
479 self.0.into_iter().next().unwrap()
480 }
481
482 /// Constructs a partial witness for a pattern given a list of
483 /// patterns expanded by the specialization step.
484 ///
485 /// When a pattern P is discovered to be useful, this function is used bottom-up
486 /// to reconstruct a complete witness, e.g., a pattern P' that covers a subset
487 /// of values, V, where each value in that set is not covered by any previously
488 /// used patterns and is covered by the pattern P'. Examples:
489 ///
490 /// left_ty: tuple of 3 elements
491 /// pats: [10, 20, _] => (10, 20, _)
492 ///
493 /// left_ty: struct X { a: (bool, &'static str), b: usize}
494 /// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
495 fn apply_constructor(
496 mut self,
497 pcx: &PatCtxt<'_>,
498 ctor: &Constructor,
499 ctor_wild_subpatterns: &Fields,
500 ) -> Self {
501 let pat = {
502 let len = self.0.len();
503 let arity = ctor_wild_subpatterns.len();
504 let pats = self.0.drain((len - arity)..).rev();
505 ctor_wild_subpatterns.replace_fields(pcx.cx, pats).apply(pcx, ctor)
506 };
507
508 self.0.push(pat);
509
510 self
511 }
512}
513
514/// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
515/// The algorithm from the paper has been modified to correctly handle empty
516/// types. The changes are:
517/// (0) We don't exit early if the pattern matrix has zero rows. We just
518/// continue to recurse over columns.
519/// (1) all_constructors will only return constructors that are statically
520/// possible. E.g., it will only return `Ok` for `Result<T, !>`.
521///
522/// This finds whether a (row) vector `v` of patterns is 'useful' in relation
523/// to a set of such vectors `m` - this is defined as there being a set of
524/// inputs that will match `v` but not any of the sets in `m`.
525///
526/// All the patterns at each column of the `matrix ++ v` matrix must have the same type.
527///
528/// This is used both for reachability checking (if a pattern isn't useful in
529/// relation to preceding patterns, it is not reachable) and exhaustiveness
530/// checking (if a wildcard pattern is useful in relation to a matrix, the
531/// matrix isn't exhaustive).
532///
533/// `is_under_guard` is used to inform if the pattern has a guard. If it
534/// has one it must not be inserted into the matrix. This shouldn't be
535/// relied on for soundness.
536fn is_useful(
537 cx: &MatchCheckCtx<'_>,
538 matrix: &Matrix,
539 v: &PatStack,
540 witness_preference: WitnessPreference,
541 is_under_guard: bool,
542 is_top_level: bool,
543) -> Usefulness {
544 let Matrix { patterns: rows, .. } = matrix;
545
546 // The base case. We are pattern-matching on () and the return value is
547 // based on whether our matrix has a row or not.
548 // NOTE: This could potentially be optimized by checking rows.is_empty()
549 // first and then, if v is non-empty, the return value is based on whether
550 // the type of the tuple we're checking is inhabited or not.
551 if v.is_empty() {
552 let ret = if rows.is_empty() {
553 Usefulness::new_useful(witness_preference)
554 } else {
555 Usefulness::new_not_useful(witness_preference)
556 };
557 return ret;
558 }
559
560 assert!(rows.iter().all(|r| r.len() == v.len()));
561
562 // FIXME(Nadrieril): Hack to work around type normalization issues (see rust-lang/rust#72476).
563 // TODO(iDawer): ty.as_reference()
564 let ty = matrix.heads().next().map_or(cx.type_of(v.head()), |r| cx.type_of(r));
565 let pcx = PatCtxt { cx, ty, is_top_level };
566
567 // If the first pattern is an or-pattern, expand it.
568 let ret = if v.head().is_or_pat(cx) {
569 //expanding or-pattern
570 let v_head = v.head();
571 let vs: Vec<_> = v.expand_or_pat(cx).collect();
572 let alt_count = vs.len();
573 // We try each or-pattern branch in turn.
574 let mut matrix = matrix.clone();
575 let usefulnesses = vs.into_iter().enumerate().map(|(i, v)| {
576 let usefulness = is_useful(cx, &matrix, &v, witness_preference, is_under_guard, false);
577 // If pattern has a guard don't add it to the matrix.
578 if !is_under_guard {
579 // We push the already-seen patterns into the matrix in order to detect redundant
580 // branches like `Some(_) | Some(0)`.
581 matrix.push(v, cx);
582 }
583 usefulness.unsplit_or_pat(i, alt_count, v_head)
584 });
585 Usefulness::merge(witness_preference, usefulnesses)
586 } else {
587 let v_ctor = v.head_ctor(cx);
588 // if let Constructor::IntRange(ctor_range) = v_ctor {
589 // // Lint on likely incorrect range patterns (#63987)
590 // ctor_range.lint_overlapping_range_endpoints(
591 // pcx,
592 // matrix.head_ctors_and_spans(cx),
593 // matrix.column_count().unwrap_or(0),
594 // hir_id,
595 // )
596 // }
597
598 // We split the head constructor of `v`.
599 let split_ctors = v_ctor.split(&pcx, matrix.head_ctors(cx));
600 // For each constructor, we compute whether there's a value that starts with it that would
601 // witness the usefulness of `v`.
602 let start_matrix = matrix;
603 let usefulnesses = split_ctors.into_iter().map(|ctor| {
604 // debug!("specialize({:?})", ctor);
605 // We cache the result of `Fields::wildcards` because it is used a lot.
606 let ctor_wild_subpatterns = Fields::wildcards(&pcx, &ctor);
607 let spec_matrix =
608 start_matrix.specialize_constructor(&pcx, &ctor, &ctor_wild_subpatterns);
609 let v = v.pop_head_constructor(&ctor_wild_subpatterns, cx);
610 let usefulness =
611 is_useful(cx, &spec_matrix, &v, witness_preference, is_under_guard, false);
612 usefulness.apply_constructor(&pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
613 });
614 Usefulness::merge(witness_preference, usefulnesses)
615 };
616
617 ret
618}
619
620/// The arm of a match expression.
621#[derive(Clone, Copy)]
622pub(crate) struct MatchArm {
623 pub(crate) pat: PatId,
624 pub(crate) has_guard: bool,
625}
626
627/// Indicates whether or not a given arm is reachable.
628#[derive(Clone, Debug)]
629pub(crate) enum Reachability {
630 /// The arm is reachable. This additionally carries a set of or-pattern branches that have been
631 /// found to be unreachable despite the overall arm being reachable. Used only in the presence
632 /// of or-patterns, otherwise it stays empty.
633 Reachable(Vec<()>),
634 /// The arm is unreachable.
635 Unreachable,
636}
637/// The output of checking a match for exhaustiveness and arm reachability.
638pub(crate) struct UsefulnessReport {
639 /// For each arm of the input, whether that arm is reachable after the arms above it.
640 pub(crate) arm_usefulness: Vec<(MatchArm, Reachability)>,
641 /// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
642 /// exhaustiveness.
643 pub(crate) non_exhaustiveness_witnesses: Vec<Pat>,
644}
645
646pub(crate) fn compute_match_usefulness(
647 cx: &MatchCheckCtx<'_>,
648 arms: &[MatchArm],
649) -> UsefulnessReport {
650 let mut matrix = Matrix::empty();
651 let arm_usefulness: Vec<_> = arms
652 .iter()
653 .copied()
654 .map(|arm| {
655 let v = PatStack::from_pattern(arm.pat);
656 let usefulness = is_useful(cx, &matrix, &v, LeaveOutWitness, arm.has_guard, true);
657 if !arm.has_guard {
658 matrix.push(v, cx);
659 }
660 let reachability = match usefulness {
661 NoWitnesses(subpats) if subpats.is_empty() => Reachability::Unreachable,
662 NoWitnesses(subpats) => {
663 Reachability::Reachable(subpats.list_unreachable_spans().unwrap())
664 }
665 WithWitnesses(..) => panic!("bug"),
666 };
667 (arm, reachability)
668 })
669 .collect();
670
671 let wild_pattern = cx.pattern_arena.borrow_mut().alloc(Pat::Wild, &cx.infer[cx.match_expr]);
672 let v = PatStack::from_pattern(wild_pattern);
673 let usefulness = is_useful(cx, &matrix, &v, LeaveOutWitness, false, true);
674 let non_exhaustiveness_witnesses = match usefulness {
675 // TODO: ConstructWitness
676 // WithWitnesses(pats) => pats.into_iter().map(Witness::single_pattern).collect(),
677 // NoWitnesses(_) => panic!("bug"),
678 NoWitnesses(subpats) if subpats.is_empty() => Vec::new(),
679 NoWitnesses(subpats) => vec![Pat::Wild],
680 WithWitnesses(..) => panic!("bug"),
681 };
682 UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
683}
684
685pub(crate) struct PatternArena {
686 arena: Arena<Pat>,
687 /// Types of expanded patterns.
688 type_of_epat: FxHashMap<PatId, Ty>,
689}
690
691impl PatternArena {
692 pub(crate) fn clone_from(pats: &Arena<Pat>) -> RefCell<Self> {
693 PatternArena { arena: pats.clone(), type_of_epat: Default::default() }.into()
694 }
695
696 fn alloc(&mut self, pat: Pat, ty: &Ty) -> PatId {
697 let id = self.arena.alloc(pat);
698 self.type_of_epat.insert(id, ty.clone());
699 id
700 }
701}
702
703impl Index<PatId> for PatternArena {
704 type Output = Pat;
705
706 fn index(&self, pat: PatId) -> &Pat {
707 &self.arena[pat]
708 }
709}
710
711mod helper {
712 use hir_def::expr::{Pat, PatId};
713
714 use super::MatchCheckCtx;
715
716 pub(super) trait PatIdExt: Sized {
717 fn is_wildcard(self, cx: &MatchCheckCtx<'_>) -> bool;
718 fn is_or_pat(self, cx: &MatchCheckCtx<'_>) -> bool;
719 fn expand_or_pat(self, cx: &MatchCheckCtx<'_>) -> Vec<Self>;
720 }
721
722 // Copy-pasted from rust/compiler/rustc_data_structures/src/captures.rs
723 /// "Signaling" trait used in impl trait to tag lifetimes that you may
724 /// need to capture but don't really need for other reasons.
725 /// Basically a workaround; see [this comment] for details.
726 ///
727 /// [this comment]: https://github.com/rust-lang/rust/issues/34511#issuecomment-373423999
728 // FIXME(eddyb) false positive, the lifetime parameter is "phantom" but needed.
729 #[allow(unused_lifetimes)]
730 pub trait Captures<'a> {}
731
732 impl<'a, T: ?Sized> Captures<'a> for T {}
733}
734
735#[test]
736fn it_works() {}