Split Usefulness::NotUseful
into two
This commit is contained in:
parent
f4f20c0663
commit
293af41790
1 changed files with 52 additions and 32 deletions
|
@ -680,24 +680,32 @@ impl SpanSet {
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
enum Usefulness<'tcx> {
|
enum Usefulness<'tcx> {
|
||||||
/// Pontentially carries a set of sub-branches that have been found to be unreachable. Used
|
/// Potentially carries a set of sub-branches that have been found to be unreachable. Used
|
||||||
/// only in the presence of or-patterns, otherwise it stays empty.
|
/// only in the presence of or-patterns, otherwise it stays empty.
|
||||||
Useful(SpanSet),
|
NoWitnesses(SpanSet),
|
||||||
/// Carries a list of witnesses of non-exhaustiveness.
|
/// When not carrying witnesses, indicates that the whole pattern is unreachable.
|
||||||
UsefulWithWitness(Vec<Witness<'tcx>>),
|
NoWitnessesFull,
|
||||||
NotUseful,
|
/// Carries a list of witnesses of non-exhaustiveness. Non-empty.
|
||||||
|
WithWitnesses(Vec<Witness<'tcx>>),
|
||||||
|
/// When carrying witnesses, indicates that the whole pattern is unreachable.
|
||||||
|
WithWitnessesEmpty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Usefulness<'tcx> {
|
impl<'tcx> Usefulness<'tcx> {
|
||||||
fn new_useful(preference: WitnessPreference) -> Self {
|
fn new_useful(preference: WitnessPreference) -> Self {
|
||||||
match preference {
|
match preference {
|
||||||
ConstructWitness => UsefulWithWitness(vec![Witness(vec![])]),
|
ConstructWitness => WithWitnesses(vec![Witness(vec![])]),
|
||||||
LeaveOutWitness => Useful(Default::default()),
|
LeaveOutWitness => NoWitnesses(Default::default()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn new_not_useful(preference: WitnessPreference) -> Self {
|
||||||
|
match preference {
|
||||||
|
ConstructWitness => WithWitnessesEmpty,
|
||||||
|
LeaveOutWitness => NoWitnessesFull,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combine usefulnesses from two branches. This is an associative operation and `NotUseful` is
|
/// Combine usefulnesses from two branches. This is an associative operation.
|
||||||
/// a unit.
|
|
||||||
fn extend(&mut self, other: Self) {
|
fn extend(&mut self, other: Self) {
|
||||||
// If we have detected some unreachable sub-branches, we only want to keep them when they
|
// If we have detected some unreachable sub-branches, we only want to keep them when they
|
||||||
// were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable
|
// were unreachable in _all_ branches. Eg. in the following, the last `true` is unreachable
|
||||||
|
@ -720,21 +728,29 @@ impl<'tcx> Usefulness<'tcx> {
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
match (&mut *self, other) {
|
match (&mut *self, other) {
|
||||||
(Useful(s), Useful(o)) => s.intersection_mut(&o),
|
(WithWitnesses(s), WithWitnesses(o)) => s.extend(o),
|
||||||
(UsefulWithWitness(s), UsefulWithWitness(o)) => s.extend(o),
|
(WithWitnessesEmpty, WithWitnesses(o)) => *self = WithWitnesses(o),
|
||||||
(_, NotUseful) => {}
|
(WithWitnesses(_), WithWitnessesEmpty) => {}
|
||||||
(NotUseful, other) => *self = other,
|
(WithWitnessesEmpty, WithWitnessesEmpty) => {}
|
||||||
(UsefulWithWitness(_), Useful(_)) | (Useful(_), UsefulWithWitness(_)) => unreachable!(),
|
|
||||||
|
(NoWitnesses(s), NoWitnesses(o)) => s.intersection_mut(&o),
|
||||||
|
(NoWitnessesFull, NoWitnesses(o)) => *self = NoWitnesses(o),
|
||||||
|
(NoWitnesses(_), NoWitnessesFull) => {}
|
||||||
|
(NoWitnessesFull, NoWitnessesFull) => {}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When trying several branches and each returns a `Usefulness`, we need to combine the
|
/// When trying several branches and each returns a `Usefulness`, we need to combine the
|
||||||
/// results together.
|
/// results together.
|
||||||
fn merge(usefulnesses: impl Iterator<Item = Self>) -> Self {
|
fn merge(pref: WitnessPreference, usefulnesses: impl Iterator<Item = Self>) -> Self {
|
||||||
let mut ret = NotUseful;
|
let mut ret = Self::new_not_useful(pref);
|
||||||
for u in usefulnesses {
|
for u in usefulnesses {
|
||||||
ret.extend(u);
|
ret.extend(u);
|
||||||
if let Useful(spans) = &ret {
|
if let NoWitnesses(spans) = &ret {
|
||||||
if spans.is_empty() {
|
if spans.is_empty() {
|
||||||
// Once we reach the empty set, more intersections won't change the result.
|
// Once we reach the empty set, more intersections won't change the result.
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -748,7 +764,7 @@ impl<'tcx> Usefulness<'tcx> {
|
||||||
/// usefulness mergeable with those from the other branches.
|
/// usefulness mergeable with those from the other branches.
|
||||||
fn unsplit_or_pat(self, this_span: Span, or_pat_spans: &[Span]) -> Self {
|
fn unsplit_or_pat(self, this_span: Span, or_pat_spans: &[Span]) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Useful(mut spans) => {
|
NoWitnesses(mut spans) => {
|
||||||
// We register the spans of the other branches of this or-pattern as being
|
// We register the spans of the other branches of this or-pattern as being
|
||||||
// unreachable from this one. This ensures that intersecting together the sets of
|
// unreachable from this one. This ensures that intersecting together the sets of
|
||||||
// spans returns what we want.
|
// spans returns what we want.
|
||||||
|
@ -759,9 +775,10 @@ impl<'tcx> Usefulness<'tcx> {
|
||||||
spans.push_nonintersecting(span);
|
spans.push_nonintersecting(span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Useful(spans)
|
NoWitnesses(spans)
|
||||||
}
|
}
|
||||||
x => x,
|
NoWitnessesFull => NoWitnessesFull,
|
||||||
|
WithWitnesses(_) | WithWitnessesEmpty => bug!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -776,7 +793,7 @@ impl<'tcx> Usefulness<'tcx> {
|
||||||
ctor_wild_subpatterns: &Fields<'p, 'tcx>,
|
ctor_wild_subpatterns: &Fields<'p, 'tcx>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match self {
|
match self {
|
||||||
UsefulWithWitness(witnesses) => {
|
WithWitnesses(witnesses) => {
|
||||||
let new_witnesses = if matches!(ctor, Constructor::Missing) {
|
let new_witnesses = if matches!(ctor, Constructor::Missing) {
|
||||||
let mut split_wildcard = SplitWildcard::new(pcx);
|
let mut split_wildcard = SplitWildcard::new(pcx);
|
||||||
split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
|
split_wildcard.split(pcx, matrix.head_ctors(pcx.cx));
|
||||||
|
@ -806,7 +823,7 @@ impl<'tcx> Usefulness<'tcx> {
|
||||||
.map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns))
|
.map(|witness| witness.apply_constructor(pcx, &ctor, ctor_wild_subpatterns))
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
UsefulWithWitness(new_witnesses)
|
WithWitnesses(new_witnesses)
|
||||||
}
|
}
|
||||||
x => x,
|
x => x,
|
||||||
}
|
}
|
||||||
|
@ -935,8 +952,11 @@ fn is_useful<'p, 'tcx>(
|
||||||
// first and then, if v is non-empty, the return value is based on whether
|
// first and then, if v is non-empty, the return value is based on whether
|
||||||
// the type of the tuple we're checking is inhabited or not.
|
// the type of the tuple we're checking is inhabited or not.
|
||||||
if v.is_empty() {
|
if v.is_empty() {
|
||||||
let ret =
|
let ret = if rows.is_empty() {
|
||||||
if rows.is_empty() { Usefulness::new_useful(witness_preference) } else { NotUseful };
|
Usefulness::new_useful(witness_preference)
|
||||||
|
} else {
|
||||||
|
Usefulness::new_not_useful(witness_preference)
|
||||||
|
};
|
||||||
debug!(?ret);
|
debug!(?ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -966,7 +986,7 @@ fn is_useful<'p, 'tcx>(
|
||||||
}
|
}
|
||||||
usefulness.unsplit_or_pat(v_span, &subspans)
|
usefulness.unsplit_or_pat(v_span, &subspans)
|
||||||
});
|
});
|
||||||
Usefulness::merge(usefulnesses)
|
Usefulness::merge(witness_preference, usefulnesses)
|
||||||
} else {
|
} else {
|
||||||
let v_ctor = v.head_ctor(cx);
|
let v_ctor = v.head_ctor(cx);
|
||||||
if let Constructor::IntRange(ctor_range) = &v_ctor {
|
if let Constructor::IntRange(ctor_range) = &v_ctor {
|
||||||
|
@ -994,7 +1014,7 @@ fn is_useful<'p, 'tcx>(
|
||||||
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false);
|
is_useful(cx, &spec_matrix, &v, witness_preference, hir_id, is_under_guard, false);
|
||||||
usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
|
usefulness.apply_constructor(pcx, start_matrix, &ctor, &ctor_wild_subpatterns)
|
||||||
});
|
});
|
||||||
Usefulness::merge(usefulnesses)
|
Usefulness::merge(witness_preference, usefulnesses)
|
||||||
};
|
};
|
||||||
debug!(?ret);
|
debug!(?ret);
|
||||||
ret
|
ret
|
||||||
|
@ -1049,9 +1069,9 @@ crate fn compute_match_usefulness<'p, 'tcx>(
|
||||||
matrix.push(v);
|
matrix.push(v);
|
||||||
}
|
}
|
||||||
let reachability = match usefulness {
|
let reachability = match usefulness {
|
||||||
Useful(spans) => Reachability::Reachable(spans),
|
NoWitnesses(spans) => Reachability::Reachable(spans),
|
||||||
NotUseful => Reachability::Unreachable,
|
NoWitnessesFull => Reachability::Unreachable,
|
||||||
UsefulWithWitness(..) => bug!(),
|
WithWitnesses(..) | WithWitnessesEmpty => bug!(),
|
||||||
};
|
};
|
||||||
(arm, reachability)
|
(arm, reachability)
|
||||||
})
|
})
|
||||||
|
@ -1061,15 +1081,15 @@ crate fn compute_match_usefulness<'p, 'tcx>(
|
||||||
let v = PatStack::from_pattern(wild_pattern);
|
let v = PatStack::from_pattern(wild_pattern);
|
||||||
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
|
let usefulness = is_useful(cx, &matrix, &v, ConstructWitness, scrut_hir_id, false, true);
|
||||||
let non_exhaustiveness_witnesses = match usefulness {
|
let non_exhaustiveness_witnesses = match usefulness {
|
||||||
NotUseful => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
|
WithWitnessesEmpty => vec![], // Wildcard pattern isn't useful, so the match is exhaustive.
|
||||||
UsefulWithWitness(pats) => {
|
WithWitnesses(pats) => {
|
||||||
if pats.is_empty() {
|
if pats.is_empty() {
|
||||||
bug!("Exhaustiveness check returned no witnesses")
|
bug!("Exhaustiveness check returned no witnesses")
|
||||||
} else {
|
} else {
|
||||||
pats.into_iter().map(|w| w.single_pattern()).collect()
|
pats.into_iter().map(|w| w.single_pattern()).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Useful(_) => bug!(),
|
NoWitnesses(_) | NoWitnessesFull => bug!(),
|
||||||
};
|
};
|
||||||
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
|
UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue