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

Commit 76ea6a6

Browse files
committed
Let WitnessMatrix decide what to report
1 parent ddfcc17 commit 76ea6a6

File tree

1 file changed

+31
-72
lines changed

1 file changed

+31
-72
lines changed

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

Lines changed: 31 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -700,15 +700,16 @@ impl<'tcx> WitnessMatrix<'tcx> {
700700
pcx: &PatCtxt<'_, '_, 'tcx>,
701701
missing_ctors: &[Constructor<'tcx>],
702702
ctor: &Constructor<'tcx>,
703+
report_when_all_missing: bool,
703704
) {
704705
if self.is_empty() {
705706
return;
706707
}
707-
if matches!(ctor, Constructor::Missing { .. }) {
708-
// We got the special `Missing` constructor, so each of the missing constructors gives a
709-
// new pattern that is not caught by the match. We list those patterns and push them
710-
// onto our current witnesses.
711-
if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
708+
if matches!(ctor, Constructor::Wildcard | Constructor::Missing) {
709+
if matches!(ctor, Constructor::Wildcard) && !report_when_all_missing {
710+
let pat = WitnessPat::wild_from_ctor(pcx, Constructor::Wildcard);
711+
self.push_pattern(&pat);
712+
} else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
712713
// We only report `_` here; listing other constructors would be redundant.
713714
let pat = WitnessPat::wild_from_ctor(pcx, Constructor::NonExhaustive);
714715
self.push_pattern(&pat);
@@ -722,8 +723,17 @@ impl<'tcx> WitnessMatrix<'tcx> {
722723
}
723724
}
724725
} else {
725-
for witness in self.0.iter_mut() {
726-
witness.apply_constructor(pcx, ctor)
726+
// We have a non-wildcard constructor.
727+
if !missing_ctors.is_empty() {
728+
// If some ctors are missing we want to only report them (so we discard non-missing
729+
// ones). This is guaranteed to report *some* witnesses because
730+
// `!missing_ctors.is_empty()` implies there is a `Missing` or `Wildcard` in
731+
// `split_ctors`.
732+
*self = Self::new_empty();
733+
} else {
734+
for witness in self.0.iter_mut() {
735+
witness.apply_constructor(pcx, ctor)
736+
}
727737
}
728738
}
729739
}
@@ -760,7 +770,6 @@ impl<'tcx> WitnessMatrix<'tcx> {
760770
fn compute_usefulness<'p, 'tcx>(
761771
cx: &MatchCheckCtxt<'p, 'tcx>,
762772
matrix: &mut Matrix<'p, 'tcx>,
763-
collect_witnesses: bool,
764773
lint_root: HirId,
765774
is_top_level: bool,
766775
) -> WitnessMatrix<'tcx> {
@@ -777,7 +786,7 @@ fn compute_usefulness<'p, 'tcx>(
777786
break;
778787
}
779788
}
780-
if useful && collect_witnesses {
789+
if useful {
781790
return WitnessMatrix::new_unit();
782791
} else {
783792
return WitnessMatrix::new_empty();
@@ -792,59 +801,17 @@ fn compute_usefulness<'p, 'tcx>(
792801
let split_set = ConstructorSet::for_ty(pcx.cx, pcx.ty).split(pcx, ctors, is_top_level);
793802
let mut split_ctors = split_set.present;
794803
let missing_ctors = split_set.missing;
804+
// We want to iterate over a full set of constructors, so if any is missing we add a wildcard.
795805
if !missing_ctors.is_empty() {
796-
// We are splitting a wildcard in order to compute its usefulness. Some constructors are
797-
// not present in the column. The first thing we note is that specializing with any of
798-
// the missing constructors would select exactly the rows with wildcards. Moreover, they
799-
// would all return equivalent results. We can therefore group them all into a
800-
// fictitious `Missing` constructor.
801-
//
802-
// As an important optimization, this function will skip all the present constructors.
803-
// This is correct because specializing with any of the present constructors would
804-
// select a strict superset of the wildcard rows, and thus would only find witnesses
805-
// already found with the `Missing` constructor.
806-
// This does mean that diagnostics are incomplete: in
807-
// ```
808-
// match x {
809-
// Some(true) => {}
810-
// }
811-
// ```
812-
// we report `None` as missing but not `Some(false)`.
813-
//
814-
// When all the constructors are missing we can equivalently return the `Wildcard`
815-
// constructor on its own. The difference between `Wildcard` and `Missing` will then
816-
// only be in diagnostics.
817-
818-
// If some constructors are missing, we typically want to report those constructors,
819-
// e.g.:
820-
// ```
821-
// enum Direction { N, S, E, W }
822-
// let Direction::N = ...;
823-
// ```
824-
// we can report 3 witnesses: `S`, `E`, and `W`.
825-
//
826-
// However, if the user didn't actually specify a constructor
827-
// in this arm, e.g., in
828-
// ```
829-
// let x: (Direction, Direction, bool) = ...;
830-
// let (_, _, false) = x;
831-
// ```
832-
// we don't want to show all 16 possible witnesses `(<direction-1>, <direction-2>,
833-
// true)` - we are satisfied with `(_, _, true)`. So if all constructors are missing we
834-
// prefer to report just a wildcard `_`.
835-
//
836-
// The exception is: if we are at the top-level, for example in an empty match, we
837-
// usually prefer to report the full list of constructors.
838-
let all_missing = split_ctors.is_empty();
839-
let report_when_all_missing =
840-
is_top_level && !super::deconstruct_pat::IntRange::is_integral(pcx.ty);
841-
let ctor = if all_missing && !report_when_all_missing {
842-
Constructor::Wildcard
806+
if split_ctors.is_empty() {
807+
split_ctors.push(Constructor::Wildcard);
843808
} else {
844-
Constructor::Missing
845-
};
846-
split_ctors.push(ctor);
809+
split_ctors.push(Constructor::Missing);
810+
}
847811
}
812+
// At the top level, we prefer to list all constructors when `_` could be reported as missing.
813+
let report_when_all_missing =
814+
is_top_level && !super::deconstruct_pat::IntRange::is_integral(pcx.ty);
848815

849816
if super::deconstruct_pat::IntRange::is_integral(ty) {
850817
for row_id in 0..matrix.len() {
@@ -874,20 +841,12 @@ fn compute_usefulness<'p, 'tcx>(
874841
// witness the usefulness of `v`.
875842
let mut ret = WitnessMatrix::new_empty();
876843
for ctor in split_ctors {
877-
// If some ctors are missing we only report those. Could report all if that's useful for
878-
// some applications.
879-
let collect_witnesses = collect_witnesses
880-
&& (missing_ctors.is_empty()
881-
|| matches!(ctor, Constructor::Wildcard | Constructor::Missing));
882844
debug!("specialize({:?})", ctor);
883845
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor);
884-
let mut witnesses = ensure_sufficient_stack(|| {
885-
compute_usefulness(cx, &mut spec_matrix, collect_witnesses, lint_root, false)
886-
});
887-
if collect_witnesses {
888-
witnesses.apply_constructor(pcx, &missing_ctors, &ctor);
889-
ret.extend(witnesses);
890-
}
846+
let mut witnesses =
847+
ensure_sufficient_stack(|| compute_usefulness(cx, &mut spec_matrix, lint_root, false));
848+
witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_when_all_missing);
849+
ret.extend(witnesses);
891850

892851
for child_row in spec_matrix.rows() {
893852
let parent_row = &mut matrix.rows[child_row.parent_row];
@@ -1028,7 +987,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
1028987
matrix.push(v);
1029988
}
1030989

1031-
let non_exhaustiveness_witnesses = compute_usefulness(cx, &mut matrix, true, lint_root, true);
990+
let non_exhaustiveness_witnesses = compute_usefulness(cx, &mut matrix, lint_root, true);
1032991
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
1033992
let arm_usefulness: Vec<_> = arms
1034993
.iter()

0 commit comments

Comments
 (0)