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

Commit 57a6f8a

Browse files
committed
Abstract over the list of WitnessStacks
1 parent 2783f1b commit 57a6f8a

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
@@ -560,21 +560,21 @@ enum Usefulness<'tcx> {
560560
NoWitnesses { useful: bool },
561561
/// Carries a list of witnesses of non-exhaustiveness. If empty, indicates that the whole
562562
/// pattern is unreachable.
563-
WithWitnesses(Vec<WitnessStack<'tcx>>),
563+
WithWitnesses(WitnessMatrix<'tcx>),
564564
}
565565

566566
impl<'tcx> Usefulness<'tcx> {
567567
fn new_useful(preference: ArmType) -> Self {
568568
match preference {
569569
// A single (empty) witness of reachability.
570-
FakeExtraWildcard => WithWitnesses(vec![WitnessStack(vec![])]),
570+
FakeExtraWildcard => WithWitnesses(WitnessMatrix::new_unit()),
571571
RealArm => NoWitnesses { useful: true },
572572
}
573573
}
574574

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

@@ -659,9 +622,9 @@ enum ArmType {
659622
RealArm,
660623
}
661624

662-
/// A witness-tuple of non-exhaustiveness for error reporting, represented as a list of patterns (in
663-
/// reverse order of construction) with wildcards inside to represent elements that can take any
664-
/// inhabitant of the type as a value.
625+
/// A partially-constructed witness of non-exhaustiveness for error reporting, represented as a list
626+
/// of patterns (in reverse order of construction) with wildcards inside to represent elements that
627+
/// can take any inhabitant of the type as a value.
665628
///
666629
/// This mirrors `PatStack`: they function similarly, except `PatStack` contains user patterns we
667630
/// are inspecting, and `WitnessStack` contains witnesses we are constructing.
@@ -719,30 +682,104 @@ impl<'tcx> WitnessStack<'tcx> {
719682
self.0.into_iter().next().unwrap()
720683
}
721684

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

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

@@ -1015,7 +1052,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
10151052
let v = PatStack::from_pattern(wild_pattern);
10161053
let usefulness = is_useful(cx, &matrix, &v, FakeExtraWildcard, lint_root, false, true);
10171054
let non_exhaustiveness_witnesses: Vec<_> = match usefulness {
1018-
WithWitnesses(pats) => pats.into_iter().map(|w| w.single_pattern()).collect(),
1055+
WithWitnesses(witness_matrix) => witness_matrix.single_column(),
10191056
NoWitnesses { .. } => bug!(),
10201057
};
10211058

0 commit comments

Comments
 (0)