Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 7009d20

Browse files
committed
Factor out or-pattern usefulness merging
1 parent 2309783 commit 7009d20

File tree

1 file changed

+81
-72
lines changed

1 file changed

+81
-72
lines changed

compiler/rustc_mir_build/src/thir/pattern/usefulness.rs

Lines changed: 81 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -648,6 +648,81 @@ impl<'tcx> Usefulness<'tcx> {
648648
!matches!(*self, NotUseful)
649649
}
650650

651+
/// When trying several branches and each returns a `Usefulness`, we need to combine the
652+
/// results together.
653+
fn merge(usefulnesses: impl Iterator<Item = (Self, Span)>, column_count: usize) -> Self {
654+
// If two branches have detected some unreachable sub-branches, we need to be careful. If
655+
// they were detected in columns that are not the current one, we want to keep only the
656+
// sub-branches that were unreachable in _all_ branches. Eg. in the following, the last
657+
// `true` is unreachable in the second branch of the first or-pattern, but not otherwise.
658+
// Therefore we don't want to lint that it is unreachable.
659+
//
660+
// ```
661+
// match (true, true) {
662+
// (true, true) => {}
663+
// (false | true, false | true) => {}
664+
// }
665+
// ```
666+
// If however the sub-branches come from the current column, they come from the inside of
667+
// the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want
668+
// to lint that the last `false` is unreachable.
669+
// ```
670+
// match None {
671+
// Some(false) => {}
672+
// None | Some(true | false) => {}
673+
// }
674+
// ```
675+
676+
// We keep track of sub-branches separately depending on whether they come from this column
677+
// or from others.
678+
let mut unreachables_this_column: FxHashSet<Span> = FxHashSet::default();
679+
let mut unreachables_other_columns: Vec<FxHashSet<Span>> = Vec::default();
680+
// Whether at least one branch is reachable.
681+
let mut any_is_useful = false;
682+
683+
for (u, span) in usefulnesses {
684+
match u {
685+
Useful(unreachables) => {
686+
if let Some((this_column, other_columns)) = unreachables.split_last() {
687+
// We keep the union of unreachables found in the first column.
688+
unreachables_this_column.extend(this_column);
689+
// We keep the intersection of unreachables found in other columns.
690+
if unreachables_other_columns.is_empty() {
691+
unreachables_other_columns = other_columns.to_vec();
692+
} else {
693+
unreachables_other_columns = unreachables_other_columns
694+
.into_iter()
695+
.zip(other_columns)
696+
.map(|(x, y)| x.intersection(&y).copied().collect())
697+
.collect();
698+
}
699+
}
700+
any_is_useful = true;
701+
}
702+
NotUseful => {
703+
unreachables_this_column.insert(span);
704+
}
705+
UsefulWithWitness(_) => {
706+
bug!(
707+
"encountered or-pat in the expansion of `_` during exhaustiveness checking"
708+
)
709+
}
710+
}
711+
}
712+
713+
if any_is_useful {
714+
let mut unreachables = if unreachables_other_columns.is_empty() {
715+
(0..column_count - 1).map(|_| FxHashSet::default()).collect()
716+
} else {
717+
unreachables_other_columns
718+
};
719+
unreachables.push(unreachables_this_column);
720+
Useful(unreachables)
721+
} else {
722+
NotUseful
723+
}
724+
}
725+
651726
fn apply_constructor<'p>(
652727
self,
653728
pcx: PatCtxt<'_, 'p, 'tcx>,
@@ -833,85 +908,19 @@ fn is_useful<'p, 'tcx>(
833908
if let Some(vs) = v.expand_or_pat() {
834909
// We expand the or pattern, trying each of its branches in turn and keeping careful track
835910
// of possible unreachable sub-branches.
836-
//
837-
// If two branches have detected some unreachable sub-branches, we need to be careful. If
838-
// they were detected in columns that are not the current one, we want to keep only the
839-
// sub-branches that were unreachable in _all_ branches. Eg. in the following, the last
840-
// `true` is unreachable in the second branch of the first or-pattern, but not otherwise.
841-
// Therefore we don't want to lint that it is unreachable.
842-
//
843-
// ```
844-
// match (true, true) {
845-
// (true, true) => {}
846-
// (false | true, false | true) => {}
847-
// }
848-
// ```
849-
// If however the sub-branches come from the current column, they come from the inside of
850-
// the current or-pattern, and we want to keep them all. Eg. in the following, we _do_ want
851-
// to lint that the last `false` is unreachable.
852-
// ```
853-
// match None {
854-
// Some(false) => {}
855-
// None | Some(true | false) => {}
856-
// }
857-
// ```
858-
859911
let mut matrix = matrix.clone();
860-
// We keep track of sub-branches separately depending on whether they come from this column
861-
// or from others.
862-
let mut unreachables_this_column: FxHashSet<Span> = FxHashSet::default();
863-
let mut unreachables_other_columns: Vec<FxHashSet<Span>> = Vec::default();
864-
// Whether at least one branch is reachable.
865-
let mut any_is_useful = false;
866-
867-
for v in vs {
868-
let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
869-
match res {
870-
Useful(unreachables) => {
871-
if let Some((this_column, other_columns)) = unreachables.split_last() {
872-
// We keep the union of unreachables found in the first column.
873-
unreachables_this_column.extend(this_column);
874-
// We keep the intersection of unreachables found in other columns.
875-
if unreachables_other_columns.is_empty() {
876-
unreachables_other_columns = other_columns.to_vec();
877-
} else {
878-
unreachables_other_columns = unreachables_other_columns
879-
.into_iter()
880-
.zip(other_columns)
881-
.map(|(x, y)| x.intersection(&y).copied().collect())
882-
.collect();
883-
}
884-
}
885-
any_is_useful = true;
886-
}
887-
NotUseful => {
888-
unreachables_this_column.insert(v.head().span);
889-
}
890-
UsefulWithWitness(_) => bug!(
891-
"encountered or-pat in the expansion of `_` during exhaustiveness checking"
892-
),
893-
}
894-
912+
let usefulnesses = vs.into_iter().map(|v| {
913+
let span = v.head().span;
914+
let u = is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false);
895915
// If pattern has a guard don't add it to the matrix.
896916
if !is_under_guard {
897917
// We push the already-seen patterns into the matrix in order to detect redundant
898918
// branches like `Some(_) | Some(0)`.
899919
matrix.push(v);
900920
}
901-
}
902-
903-
return if any_is_useful {
904-
let mut unreachables = if unreachables_other_columns.is_empty() {
905-
let n_columns = v.len();
906-
(0..n_columns - 1).map(|_| FxHashSet::default()).collect()
907-
} else {
908-
unreachables_other_columns
909-
};
910-
unreachables.push(unreachables_this_column);
911-
Useful(unreachables)
912-
} else {
913-
NotUseful
914-
};
921+
(u, span)
922+
});
923+
return Usefulness::merge(usefulnesses, v.len());
915924
}
916925

917926
// FIXME(Nadrieril): Hack to work around type normalization issues (see #72476).

0 commit comments

Comments
 (0)