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

Commit 25696cc

Browse files
committed
Abstract over the list of WitnessStacks
1 parent 8ad33a8 commit 25696cc

File tree

1 file changed

+106
-69
lines changed

1 file changed

+106
-69
lines changed

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

Lines changed: 106 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -564,21 +564,21 @@ enum Usefulness<'tcx> {
564564
NoWitnesses { useful: bool },
565565
/// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
566566
/// pattern is unreachable.
567-
WithWitnesses(Vec<WitnessStack<'tcx>>),
567+
WithWitnesses(WitnessMatrix<'tcx>),
568568
}
569569

570570
impl<'tcx> Usefulness<'tcx> {
571571
fn new_useful(preference: ArmType) -> Self {
572572
match preference {
573573
// A single (empty) witness of reachability.
574-
FakeExtraWildcard => WithWitnesses(vec![WitnessStack(vec![])]),
574+
FakeExtraWildcard => WithWitnesses(WitnessMatrix::unit_witness()),
575575
RealArm => NoWitnesses { useful: true },
576576
}
577577
}
578578

579579
fn new_not_useful(preference: ArmType) -> Self {
580580
match preference {
581-
FakeExtraWildcard => WithWitnesses(vec![]),
581+
FakeExtraWildcard => WithWitnesses(WitnessMatrix::empty()),
582582
RealArm => NoWitnesses { useful: false },
583583
}
584584
}
@@ -607,53 +607,16 @@ impl<'tcx> Usefulness<'tcx> {
607607
/// that makes sense for the matrix pre-specialization. This new usefulness can then be merged
608608
/// with the results of specializing with the other constructors.
609609
fn apply_constructor(
610-
self,
610+
mut self,
611611
pcx: &PatCtxt<'_, '_, 'tcx>,
612612
matrix: &Matrix<'_, 'tcx>, // used to compute missing ctors
613613
ctor: &Constructor<'tcx>,
614614
) -> Self {
615-
match self {
616-
NoWitnesses { .. } => self,
617-
WithWitnesses(ref witnesses) if witnesses.is_empty() => self,
618-
WithWitnesses(witnesses) => {
619-
let new_witnesses = if let Constructor::Missing { .. } = ctor {
620-
let mut missing = ConstructorSet::for_ty(pcx.cx, pcx.ty)
621-
.compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
622-
if missing.iter().any(|c| c.is_non_exhaustive()) {
623-
// We only report `_` here; listing other constructors would be redundant.
624-
missing = vec![Constructor::NonExhaustive];
625-
}
626-
627-
// We got the special `Missing` constructor, so each of the missing constructors
628-
// gives a new pattern that is not caught by the match.
629-
// We construct for each missing constructor a version of this constructor with
630-
// wildcards for fields, i.e. that matches everything that can be built with it.
631-
// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get
632-
// the pattern `Some(_)`.
633-
let new_patterns: Vec<WitnessPat<'_>> = missing
634-
.into_iter()
635-
.map(|missing_ctor| WitnessPat::wild_from_ctor(pcx, missing_ctor.clone()))
636-
.collect();
637-
638-
witnesses
639-
.into_iter()
640-
.flat_map(|witness| {
641-
new_patterns.iter().map(move |pat| {
642-
let mut stack = witness.clone();
643-
stack.0.push(pat.clone());
644-
stack
645-
})
646-
})
647-
.collect()
648-
} else {
649-
witnesses
650-
.into_iter()
651-
.map(|witness| witness.apply_constructor(pcx, ctor))
652-
.collect()
653-
};
654-
WithWitnesses(new_witnesses)
655-
}
615+
match &mut self {
616+
NoWitnesses { .. } => {}
617+
WithWitnesses(witnesses) => witnesses.apply_constructor(pcx, matrix, ctor),
656618
}
619+
self
657620
}
658621
}
659622

@@ -663,9 +626,9 @@ enum ArmType {
663626
RealArm,
664627
}
665628

666-
/// A witness-tuple of non-exhaustiveness for error reporting, represented as a list of patterns (in
667-
/// reverse order of construction) with wildcards inside to represent elements that can take any
668-
/// inhabitant of the type as a value.
629+
/// A partially-constructed witness of non-exhaustiveness for error reporting, represented as a list
630+
/// of patterns (in reverse order of construction) with wildcards inside to represent elements that
631+
/// can take any inhabitant of the type as a value.
669632
///
670633
/// This mirrors `PatStack`: they function similarly, except `PatStack` contains user patterns we
671634
/// are inspecting, and `WitnessStack` contains witnesses we are constructing.
@@ -723,30 +686,104 @@ impl<'tcx> WitnessStack<'tcx> {
723686
self.0.into_iter().next().unwrap()
724687
}
725688

726-
/// Constructs a partial witness for a pattern given a list of
727-
/// patterns expanded by the specialization step.
728-
///
729-
/// When a pattern P is discovered to be useful, this function is used bottom-up
730-
/// to reconstruct a complete witness, e.g., a pattern P' that covers a subset
731-
/// of values, V, where each value in that set is not covered by any previously
732-
/// used patterns and is covered by the pattern P'. Examples:
689+
/// Reverses specialization by the `Missing` constructor by pushing a whole new pattern.
690+
fn push_pattern(&mut self, pat: WitnessPat<'tcx>) {
691+
self.0.push(pat);
692+
}
693+
694+
/// Reverses specialization. Given a witness obtained after specialization, this constructs a
695+
/// new witness valid for before specialization. Examples:
733696
///
734-
/// left_ty: tuple of 3 elements
735-
/// pats: [10, 20, _] => (10, 20, _)
697+
/// ctor: tuple of 2 elements
698+
/// pats: [false, "foo", _, true]
699+
/// result: [(false, "foo"), _, true]
736700
///
737-
/// left_ty: struct X { a: (bool, &'static str), b: usize}
738-
/// pats: [(false, "foo"), 42] => X { a: (false, "foo"), b: 42 }
739-
fn apply_constructor(mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
740-
let pat = {
741-
let len = self.0.len();
742-
let arity = ctor.arity(pcx);
743-
let fields = self.0.drain((len - arity)..).rev().collect();
744-
WitnessPat::new(ctor.clone(), fields, pcx.ty)
745-
};
746-
701+
/// ctor: Enum::Variant { a: (bool, &'static str), b: usize}
702+
/// pats: [(false, "foo"), _, true]
703+
/// result: [Enum::Variant { a: (false, "foo"), b: _ }, true]
704+
fn apply_constructor(&mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) {
705+
let len = self.0.len();
706+
let arity = ctor.arity(pcx);
707+
let fields = self.0.drain((len - arity)..).rev().collect();
708+
let pat = WitnessPat::new(ctor.clone(), fields, pcx.ty);
747709
self.0.push(pat);
710+
}
711+
}
748712

749-
self
713+
/// Represents a set of partially-constructed witnesses of non-exhaustiveness for error reporting.
714+
/// This has similar invariants as `Matrix` does.
715+
/// Throughout the exhaustiveness phase of the algorithm, `is_useful` maintains the invariant that
716+
/// the union of the `Matrix` and the `WitnessMatrix` together matches the type exhaustively. By the
717+
/// end of the algorithm, this has a single column, which contains the patterns that are missing for
718+
/// the match to be exhaustive.
719+
#[derive(Debug, Clone)]
720+
pub struct WitnessMatrix<'tcx>(Vec<WitnessStack<'tcx>>);
721+
722+
impl<'tcx> WitnessMatrix<'tcx> {
723+
/// New matrix with no rows.
724+
fn empty() -> Self {
725+
WitnessMatrix(vec![])
726+
}
727+
/// New matrix with one row and no columns.
728+
fn unit_witness() -> Self {
729+
WitnessMatrix(vec![WitnessStack(vec![])])
730+
}
731+
732+
/// Whether this has any rows.
733+
fn is_empty(&self) -> bool {
734+
self.0.is_empty()
735+
}
736+
/// Asserts that there is a single column and returns the patterns in it.
737+
fn single_column(self) -> Vec<WitnessPat<'tcx>> {
738+
self.0.into_iter().map(|w| w.single_pattern()).collect()
739+
}
740+
741+
/// Reverses specialization by the `Missing` constructor by pushing a whole new pattern.
742+
fn push_pattern(&mut self, pat: &WitnessPat<'tcx>) {
743+
for witness in self.0.iter_mut() {
744+
witness.push_pattern(pat.clone())
745+
}
746+
}
747+
748+
/// Reverses specialization by `ctor`.
749+
fn apply_constructor(
750+
&mut self,
751+
pcx: &PatCtxt<'_, '_, 'tcx>,
752+
matrix: &Matrix<'_, 'tcx>, // used to compute missing ctors
753+
ctor: &Constructor<'tcx>,
754+
) {
755+
if self.is_empty() {
756+
return;
757+
}
758+
if matches!(ctor, Constructor::Missing { .. }) {
759+
let missing_ctors = ConstructorSet::for_ty(pcx.cx, pcx.ty)
760+
.compute_missing(pcx, matrix.heads().map(DeconstructedPat::ctor));
761+
// We got the special `Missing` constructor, so each of the missing constructors gives a
762+
// new pattern that is not caught by the match. We list those patterns and push them
763+
// onto our current witnesses.
764+
if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
765+
// We only report `_` here; listing other constructors would be redundant.
766+
let pat = WitnessPat::wild_from_ctor(pcx, Constructor::NonExhaustive);
767+
self.push_pattern(&pat);
768+
} else {
769+
let old_witnesses = std::mem::replace(self, Self::empty());
770+
for ctor in missing_ctors {
771+
let pat = WitnessPat::wild_from_ctor(pcx, ctor.clone());
772+
let mut witnesses_with_missing_ctor = old_witnesses.clone();
773+
witnesses_with_missing_ctor.push_pattern(&pat);
774+
self.extend(witnesses_with_missing_ctor)
775+
}
776+
}
777+
} else {
778+
for witness in self.0.iter_mut() {
779+
witness.apply_constructor(pcx, ctor)
780+
}
781+
}
782+
}
783+
784+
/// Merges the rows of two witness matrices. Their column types must match.
785+
fn extend(&mut self, other: Self) {
786+
self.0.extend(other.0)
750787
}
751788
}
752789

@@ -1144,7 +1181,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
11441181
let v = PatStack::from_pattern(wild_pattern);
11451182
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, false, true);
11461183
let non_exhaustiveness_witnesses: Vec<_> = match usefulness {
1147-
WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
1184+
WithWitnesses(witness_matrix) => witness_matrix.single_column(),
11481185
NoWitnesses { .. } => bug!(),
11491186
};
11501187

0 commit comments

Comments
 (0)